A mass of changes :D

This commit is contained in:
WolverinDEV 2018-04-11 17:56:09 +02:00
parent ca1accad57
commit e357cf5aa6
92 changed files with 275276 additions and 99285 deletions

6
.idea/Web-Client.iml generated
View file

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/asm/generated" />
<excludeFolder url="file://$MODULE_DIR$/enviroment" />
<excludeFolder url="file://$MODULE_DIR$/generated" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="adapter-latest" level="application" />

1227
.idea/workspace.xml generated

File diff suppressed because it is too large Load diff

View file

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WSSTest</title>
</head>
<body>
<!-- <iframe src="https://localhost:19974"></iframe> -->
<script src="WSSTest.js"></script>
</body>
</html>

View file

@ -1,23 +0,0 @@
/*
let oldError = console.error;
console.error = function () {
oldError("Got error:");
oldError(arguments);
};
let wss = new WebSocket("wss:localhost:4433");
wss.onclose = ev => console.log(ev);
wss.onerror = ev => console.log(ev);
wss.onmessage = ev => console.log(ev);
wss.onopen = ev => console.log(ev);
setTimeout(() => {
//document.location.assign("https://localhost:44330");
}, 1000);
setTimeout(() => {
let win = window.open("https://localhost:44330");
if(win) {
win.close();
}
}, 1000);
*/

27
animates/A.html Normal file
View file

@ -0,0 +1,27 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<script type="text/javascript" src="https://adsterra.com/libs/header_index/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="https://adsterra.com/libs/header_index/pixi.min.js"></script>
<script type="text/javascript" src="https://adsterra.com/libs/header_index/animationFrame.js?ver=201703051115"></script>
<script type="text/javascript" src="https://adsterra.com/libs/header_index/nodes.js?ver=201703051115"></script>
</head>
<style>body {
margin: 0;
background-color: transparent;
}</style>
<body>
<header>
<script>
Nodes.multipleInit([{
"post_name": "ADSTERRA",
"drawnImage": "TeaSpeak32b.png", //https://orig00.deviantart.net/45aa/f/2015/323/9/9/letter_a_by_hillygon-d9h8c6a.jpg | TeaSpeak32b.png
"linkTitle": "ADSTERRA",
"particleDensity": "5",
"particleWidth": "0.4",
"particleHeight": "0.4"
}]);
</script>
</header>
</body>
</html>

View file

@ -3,16 +3,16 @@ project(TeaWeb-Native)
set(CMAKE_CXX_COMPILER "emcc")
set(CMAKE_C_COMPILER "emcc")
set(CMAKE_C_LINK_EXECUTABLE "emcc")
set(CMAKE_CXX_FLAGS "-s ASSERTIONS=2") #-s WASM=1
set(CMAKE_CXX_FLAGS "-s ASSERTIONS=2 -s ALLOW_MEMORY_GROWTH=1") #-s WASM=1 -O2
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") #
set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]'") #
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_definitions(-DLTM_DESC)
include_directories(libraries/opus/include/)
include_directories(libraries/tommath/)
include_directories(libraries/tomcrypt/src/headers)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/generated/")
add_executable(TeaWeb-Native.js src/WebASMTest.cpp)
target_link_libraries(TeaWeb-Native.js ${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/.libs/libopus.a)
#Adding directories to PATH:
#PATH += /home/wolverindev/wgit/emscripten-sdk
#Setting environment variables:
#EMSDK = /home/wolverindev/wgit/emscripten-sdk
#EM_CONFIG = /home/wolverindev/.emscripten
add_executable(TeaWeb-Native.js src/CodecOpus.cpp src/TeamSpeakIdentity.cpp src/Identity.cpp src/INIReader.h)
target_link_libraries(TeaWeb-Native.js ${CMAKE_CURRENT_SOURCE_DIR}/libraries/opus/.libs/libopus.a ${CMAKE_CURRENT_SOURCE_DIR}/libraries/tomcrypt/libtomcrypt.a ${CMAKE_CURRENT_SOURCE_DIR}/libraries/tommath/build/libtommathStatic.a)

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
Subproject commit 655cc54c564b84ef2827f0b2152ce3811046201e

View file

@ -1,4 +1,4 @@
#include <libs/opus/include/opus.h>
#include <opus.h>
#include <emscripten.h>
#include <string>
@ -9,6 +9,7 @@ extern "C" {
OpusDecoder* decoder = nullptr;
size_t channelCount = 1;
int opusType = OPUS_APPLICATION_AUDIO;
};
EMSCRIPTEN_KEEPALIVE
@ -17,9 +18,8 @@ extern "C" {
auto codec = new OpusHandle{};
int error = 0;
codec->decoder = opus_decoder_create(48000, channelCount, &error);
printf("Status %d\n", error);
codec->encoder = opus_encoder_create(48000, channelCount, type, &error);
printf("Status %d Channel %d\n", error, codec->channelCount);
codec->opusType = type;
return codec;
}
@ -52,11 +52,22 @@ extern "C" {
EMSCRIPTEN_KEEPALIVE
int codec_opus_changeApplication(OpusHandle* handle, int type) {
handle->opusType = type;
if(type != OPUS_APPLICATION_VOIP && type != OPUS_APPLICATION_AUDIO && type != OPUS_APPLICATION_RESTRICTED_LOWDELAY)
return 1;
return opus_encoder_ctl(handle->encoder, OPUS_SET_APPLICATION(type));
}
EMSCRIPTEN_KEEPALIVE
int codec_opus_reset(OpusHandle* handle) {
if(handle->encoder) opus_encoder_destroy(handle->encoder);
if(handle->decoder) opus_decoder_destroy(handle->decoder);
int error = 0;
handle->decoder = opus_decoder_create(48000, handle->channelCount, &error);
handle->encoder = opus_encoder_create(48000, handle->channelCount, handle->opusType, &error);
return 1;
}
/*
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));

458
asm/src/INIReader.h Normal file
View file

@ -0,0 +1,458 @@
// Read an INI file into easy-to-access name/value pairs.
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
// Go to the project home page for more info:
//
// https://github.com/benhoyt/inih
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Typedef for prototype of handler function. */
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
inline int ini_parse_message(const std::string& message, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sstream>
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
inline static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
inline static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
inline static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
inline static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through stream line by line */
while (reader(line, INI_MAX_LINE, stream) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python configparser, allow both ; and # comments at the
start of a line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(start, NULL);
if (*end)
*end = '\0';
rstrip(start);
#endif
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
inline int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
inline int ini_parse_message(const std::string& message, ini_handler handler, void* user)
{
std::istringstream stream(message);
return ini_parse_stream((ini_reader) [](char* str, int num, void* ptrStream) {
auto stream = (std::istringstream*) ptrStream;
stream->getline(str, num);
return !!*stream ? str : nullptr;
}, &stream, handler, user);
}
/* See documentation in header file. */
inline int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}
#endif /* __INI_H__ */
#ifndef __INIREADER_H__
#define __INIREADER_H__
#include <map>
#include <set>
#include <string>
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class INIReader
{
public:
// Empty Constructor
INIReader() {};
// Construct INIReader and parse given filename. See ini.h for more info
// about the parsing.
INIReader(std::string filename, bool raw = false);
// Return the result of ini_parse(), i.e., 0 on success, line number of
// first error on parse error, or -1 on file open error.
int ParseError() const;
// Return the list of sections found in ini file
std::set<std::string> Sections();
// Get a string value from INI file, returning default_value if not found.
std::string Get(std::string section, std::string name,
std::string default_value);
// Get an integer (long) value from INI file, returning default_value if
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
long GetInteger(std::string section, std::string name, long default_value);
// Get a real (floating point double) value from INI file, returning
// default_value if not found or not a valid floating point value
// according to strtod().
double GetReal(std::string section, std::string name, double default_value);
// Get a boolean value from INI file, returning default_value if not found or if
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
// and valid false values are "false", "no", "off", "0" (not case sensitive).
bool GetBoolean(std::string section, std::string name, bool default_value);
private:
int _error;
std::map<std::string, std::string> _values;
std::set<std::string> _sections;
static std::string MakeKey(std::string section, std::string name);
static int ValueHandler(void* user, const char* section, const char* name,
const char* value);
};
#endif // __INIREADER_H__
#ifndef __INIREADER__
#define __INIREADER__
#include <algorithm>
#include <cctype>
#include <cstdlib>
using std::string;
inline INIReader::INIReader(string data, bool raw)
{
if(raw)
_error = ini_parse_message(data, ValueHandler, this);
else
_error = ini_parse(data.c_str(), ValueHandler, this);
}
inline int INIReader::ParseError() const
{
return _error;
}
inline std::set<string> INIReader::Sections()
{
return _sections;
}
inline string INIReader::Get(string section, string name, string default_value)
{
string key = MakeKey(section, name);
return _values.count(key) ? _values[key] : default_value;
}
inline long INIReader::GetInteger(string section, string name, long default_value)
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
long n = strtol(value, &end, 0);
return end > value ? n : default_value;
}
inline double INIReader::GetReal(string section, string name, double default_value)
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
double n = strtod(value, &end);
return end > value ? n : default_value;
}
inline bool INIReader::GetBoolean(string section, string name, bool default_value)
{
string valstr = Get(section, name, "");
// Convert to lower case to make string comparisons case-insensitive
std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
return true;
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
return false;
else
return default_value;
}
inline string INIReader::MakeKey(string section, string name)
{
string key = section + "=" + name;
// Convert to lower case to make section/name lookups case-insensitive
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
return key;
}
inline int INIReader::ValueHandler(void* user, const char* section, const char* name,
const char* value)
{
INIReader* reader = (INIReader*)user;
string key = MakeKey(section, name);
if (reader->_values[key].size() > 0)
reader->_values[key] += "\n";
reader->_values[key] += value;
reader->_sections.insert(section);
return 1;
}
#endif // __INIREADER__

283
asm/src/Identity.cpp Normal file
View file

@ -0,0 +1,283 @@
#include "Identity.h"
#include <iostream>
#include "base64.h"
#define SHA_DIGEST_LENGTH 20
#define ECC_TYPE_INDEX 5
static const char *TSKEY =
"b9dfaa7bee6ac57ac7b65f1094a1c155"
"e747327bc2fe5d51c512023fe54a2802"
"01004e90ad1daaae1075d53b7d571c30"
"e063b5a62a4a017bb394833aa0983e6e";
using namespace std;
inline int SHA1(const char* input, size_t length, char* result) {
hash_state ctx = {};
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*) input, length) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*) result) != CRYPT_OK)
{ return -1; }
return 0;
}
static int decriptIdentity(char *data, uint32_t length) {
int dataSize = std::min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
char hash[SHA_DIGEST_LENGTH];
//if(SHA1(data + 20, strlen(data + 20), hash) < 0) return -1;
hash_state ctx = {};
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
return 0;
}
static int encriptIdentity(char *data, uint32_t length) {
char hash[SHA_DIGEST_LENGTH];
//if(SHA1(data, length, hash) < 0) return -1;
hash_state ctx;
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
int dataSize = std::min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
return 0;
}
namespace ts {
Identity* Identity::createNew() {
auto result = new Identity();
prng_state rndState = {};
memset(&rndState, 0, sizeof(prng_state));
int err;
result->keyPair = new ecc_key;
//cout << " -> " << find_prng("sprng") << endl;
if((err = ecc_make_key_ex(&rndState, find_prng("sprng"), result->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
printf("Cant create a new identity (Keygen)\n");
printf("Message: %s\n", error_to_string(err));
delete result;
return nullptr;
}
return result;
}
Identity* Identity::parse(const std::string& data, std::string& error) {
int vindex = data.find('V');
if(vindex <= 0) {
error = "Invalid structure";
return nullptr;
}
auto slevel = data.substr(0, vindex);
if(slevel.find_first_not_of("0123456789") != std::string::npos) {
error = "Invalid offset (" + slevel + ")";
return nullptr;
}
mp_int keyOffset{};
mp_init(&keyOffset);
mp_read_radix(&keyOffset, slevel.data(), 10);
auto keyData = data.substr(vindex + 1);
keyData = base64::decode(keyData);
if(encriptIdentity(&keyData[0], keyData.length()) < 0) {
error = "Could not decrypt key";
return nullptr;
}
auto identity = new Identity(base64::decode(keyData), keyOffset, keyOffset);
if(!identity->keyPair) {
error = "Could not load key";
delete identity;
return nullptr;
}
printf("X: %s | %s\n", slevel.c_str(), identity->lastValidKeyOffsetString().c_str());
return identity;
}
Identity::Identity(const std::string& asnStruct, mp_int keyOffset, mp_int lastCheckedOffset) {
this->keyOffset = keyOffset;
this->lastCheckedOffset = lastCheckedOffset;
importKey(asnStruct);
mp_init_copy(&this->keyOffset, &keyOffset);
mp_init_copy(&this->lastCheckedOffset, &lastCheckedOffset);
}
Identity::Identity() {
mp_init_multi(&this->keyOffset, &this->lastCheckedOffset, nullptr);
this->keyPair = nullptr;
}
Identity::~Identity() {
delete this->keyPair;
this->keyPair = nullptr;
mp_clear_multi(&this->keyOffset, &this->lastCheckedOffset, nullptr);
}
void Identity::importKey(std::string asnStruct) {
this->keyPair = new ecc_key;
int err;
if((err = ecc_import_ex((const unsigned char *) asnStruct.data(), asnStruct.length(), this->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
delete this->keyPair;
this->keyPair = nullptr;
printf("Cant import identity from asn structure\n");
printf("Message: %s\n", error_to_string(err));
return;
}
}
std::string Identity::exportIdentity() {
std::string data = privateKey();
decriptIdentity((char *) data.data(), data.length());
return this->lastValidKeyOffsetString() + "V" + base64_encode(data);
}
std::string Identity::uid() {
char buffer[SHA_DIGEST_LENGTH];
auto key = this->publicKey();
SHA1(key.data(), key.length(), buffer);
return base64::encode(buffer, SHA_DIGEST_LENGTH);
}
inline string hex(string input, char beg, char end){
assert(end - beg == 16);
int len = input.length() * 2;
char output[len];
int idx = 0;
for(int index = 0; index < input.length(); index++){
char elm = input[index];
output[idx++] = static_cast<char>(beg + ((elm >> 4) & 0x0F));
output[idx++] = static_cast<char>(beg + ((elm & 0x0F) >> 0));
}
return string(output, len);
}
std::string Identity::avatarId() {
return hex(base64::decode(this->uid()), 'a', 'q');
}
std::string Identity::publicKey() {
assert(this->keyPair);
ulong32 bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PUBLIC, this->keyPair);
return base64_encode(std::string(buffer, bufferLength));
}
std::string Identity::privateKey() {
assert(this->keyPair);
ulong32 bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PRIVATE, this->keyPair);
return base64_encode(std::string(buffer, bufferLength));
}
ecc_key& Identity::getPrivateKey() {
return *keyPair;
}
#define MaxUlongString 20
bool Identity::improveSecurityLevel(int target) {
auto publicKey = this->publicKey();
char hashBuffer[publicKey.length() + MaxUlongString];
memcpy(hashBuffer, publicKey.data(), publicKey.length());
if(mp_cmp(&this->lastCheckedOffset, &this->keyOffset) < 0)
mp_copy(&this->keyOffset, &this->lastCheckedOffset);
int best = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
while(true){
if(best >= target) return true;
int currentLevel = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
if(currentLevel >= best){
this->keyOffset = this->lastCheckedOffset;
best = currentLevel;
}
mp_add_d(&this->lastCheckedOffset, 1, &this->lastCheckedOffset);
}
}
int Identity::getSecurityLevel() {
auto length = publicKey().length();
char hashBuffer[length + MaxUlongString];
auto publicKey = this->publicKey();
memcpy(hashBuffer, publicKey.data(), publicKey.length());
return getSecurityLevel(hashBuffer, publicKey.length(), this->keyOffset);
}
int Identity::getSecurityLevel(char *hashBuffer, size_t keyLength, mp_int offset) {
char numBuffer[MaxUlongString];
mp_todecimal(&offset, numBuffer);
/*
int numLen = 0;
do {
numBuffer[numLen] = '0' + (offset % 10);
offset /= 10;
numLen++;
} while(offset > 0);
for(int i = 0; i < numLen; i++)
hashBuffer[keyLength + i] = numBuffer[numLen - (i + 1)];
*/
auto numLen = strlen(numBuffer);
memcpy(&hashBuffer[keyLength], numBuffer, numLen);
char shaBuffer[SHA_DIGEST_LENGTH];
SHA1(hashBuffer, keyLength + numLen, shaBuffer);
//Leading zero bits
int zeroBits = 0;
int i;
for(i = 0; i < SHA_DIGEST_LENGTH; i++)
if(shaBuffer[i] == 0) zeroBits += 8;
else break;
if(i < SHA_DIGEST_LENGTH)
for(int bit = 0; bit < 8; bit++)
if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++;
else break;
return zeroBits;
}
}

59
asm/src/Identity.h Normal file
View file

@ -0,0 +1,59 @@
#pragma once
#include <tomcrypt.h>
#include <string>
#include <tommath.h>
namespace ts {
class Identity {
inline std::string toString(const mp_int& num) {
char buffer[1024];
mp_todecimal(&num, buffer);
return std::string(buffer);
}
public:
static Identity* createNew();
static Identity* parse(const std::string&, std::string&);
Identity(const std::string& asnStruct,mp_int keyOffset,mp_int lastCheckedOffset);
~Identity();
bool valid(){ return keyPair != nullptr; }
std::string uid();
std::string avatarId();
std::string publicKey();
std::string privateKey();
std::string exportIdentity();
ecc_key* getKeyPair(){
return keyPair;
}
ecc_key& getPrivateKey();
bool improveSecurityLevel(int target);
int getSecurityLevel();
mp_int lastValidKeyOffset(){ return keyOffset; }
mp_int lastTestedKeyOffset(){ return lastCheckedOffset; }
std::string lastValidKeyOffsetString(){
return toString(this->lastValidKeyOffset());
}
std::string lastTestedKeyOffsetString(){
return toString(this->lastTestedKeyOffset());
}
private:
Identity();
int getSecurityLevel(char* hasBuffer, size_t keyLength, mp_int offset);
void importKey(std::string asn1);
ecc_key* keyPair = nullptr;
mp_int keyOffset;
mp_int lastCheckedOffset;
};
}

View file

@ -0,0 +1,136 @@
#include <cstdio>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <tomcrypt.h>
#include <cmath>
#include <iostream>
#include "Identity.h"
#include "base64.h"
#define INI_MAX_LINE 1024
#include "INIReader.h"
using namespace emscripten;
using namespace std;
extern "C" {
std::string errorMessage = "";
inline const char* cstr(const std::string& message) {
auto buffer = (char*) malloc(message.length() + 1);
cout << "Allocating at " << (void*) buffer << endl;
buffer[message.length()] = '\0';
memcpy(buffer, message.data(), message.length());
return buffer;
}
EMSCRIPTEN_KEEPALIVE
const char* last_error_message() {
return cstr(errorMessage);
};
EMSCRIPTEN_KEEPALIVE
void destroy_string(const char* str) {
cout << "Deallocating at " << (void*) str << endl;
if(str) free((void *) str);
};
inline void clear_error() { errorMessage = ""; }
EMSCRIPTEN_KEEPALIVE
int tomcrypt_initialize() {
init_LTM();
if(register_prng(&sprng_desc) == -1) {
printf("could not setup prng\n");
return EXIT_FAILURE;
}
if (register_cipher(&rijndael_desc) == -1) {
printf("could not setup rijndael\n");
return EXIT_FAILURE;
}
cout << "Initialized!" << endl;
return 0;
}
EMSCRIPTEN_KEEPALIVE
void* parse_identity(const char* input) {
cout << "Got messsage: " << input << endl;
clear_error();
return ts::Identity::parse(input, errorMessage);
}
EMSCRIPTEN_KEEPALIVE
void* parse_identity_file(const char* input) {
clear_error();
INIReader reader(input, true);
if(reader.ParseError() != 0) {
errorMessage = "Could not parse file " + to_string(reader.ParseError());
return nullptr;
}
auto identity = reader.Get("Identity", "identity", "");
if(!identity.empty() && identity[0] == '"')
identity = identity.substr(1);
if(!identity.empty() && identity.back() == '"')
identity = identity.substr(0, identity.length() - 1);
if(identity.empty()) {
errorMessage = "Mussing identity value at Identity::identity";
return nullptr;
}
return ts::Identity::parse(identity, errorMessage);
}
#define IDENTITIEFY(_ret) \
auto identity = dynamic_cast<ts::Identity*>((ts::Identity*) ptrIdentity); \
if(!identity) { \
errorMessage = "Invalid identity pointer!"; \
return _ret; \
}
EMSCRIPTEN_KEEPALIVE
void delete_identity(void* ptrIdentity) {
IDENTITIEFY(;);
delete identity;
}
EMSCRIPTEN_KEEPALIVE
const char* identity_security_level(void* ptrIdentity) {
IDENTITIEFY("");
return cstr(std::to_string(identity->getSecurityLevel()));
}
EMSCRIPTEN_KEEPALIVE
const char* identity_export(void* ptrIdentity) {
IDENTITIEFY("");
return cstr(identity->exportIdentity());
}
EMSCRIPTEN_KEEPALIVE
const char* identity_key_public(void* ptrIdentity) {
IDENTITIEFY("");
return cstr(identity->publicKey());
}
EMSCRIPTEN_KEEPALIVE
const char* identity_uid(void* ptrIdentity) {
IDENTITIEFY("");
return cstr(identity->uid());
}
EMSCRIPTEN_KEEPALIVE
const char* identity_sign(void* ptrIdentity, const char* message, int length) {
IDENTITIEFY("");
ulong32 bufferLength = 128;
char signBuffer[bufferLength];
prng_state rndState = {};
memset(&rndState, 0, sizeof(prng_state));
auto state = ecc_sign_hash((const unsigned char*) message, length, reinterpret_cast<unsigned char *>(signBuffer), &bufferLength, &rndState, find_prng("sprng"), identity->getKeyPair());
if(state != CRYPT_OK) {
errorMessage = "Could not sign message (" + std::string(error_to_string(state)) + "|" + std::to_string(state) + ")";
return "";
}
return cstr(base64::encode(signBuffer, bufferLength));
}
}

59
asm/src/base64.h Normal file
View file

@ -0,0 +1,59 @@
#pragma once
#include <string>
#include <tomcrypt.h>
#include <iostream>
namespace base64 {
/**
* Encodes a given string in Base64
* @param input The input string to Base64-encode
* @param inputSize The size of the input to decode
* @return A Base64-encoded version of the encoded string
*/
inline std::string encode(const char* input, const unsigned long inputSize) {
auto outlen = static_cast<unsigned long>(inputSize + (inputSize / 3.0) + 16);
auto outbuf = new unsigned char[outlen]; //Reserve output memory
if(base64_encode((unsigned char*) input, inputSize, outbuf, &outlen) != CRYPT_OK){
std::cerr << "Invalid input '" << input << "'" << std::endl;
return "";
}
std::string ret((char*) outbuf, outlen);
delete[] outbuf;
return ret;
}
/**
* Encodes a given string in Base64
* @param input The input string to Base64-encode
* @return A Base64-encoded version of the encoded string
*/
inline std::string encode(const std::string& input) { return encode(input.c_str(), input.size()); }
/**
* Decodes a Base64-encoded string.
* @param input The input string to decode
* @return A string (binary) that represents the Base64-decoded data of the input
*/
inline std::string decode(const char* input, ulong32 size) {
auto out = new unsigned char[size];
if(base64_strict_decode((unsigned char*) input, size, out, &size) != CRYPT_OK){
std::cerr << "Invalid base 64 string '" << input << "'" << std::endl;
return "";
}
std::string ret((char*) out, size);
delete[] out;
return ret;
}
/**
* Decodes a Base64-encoded string.
* @param input The input string to decode
* @return A string (binary) that represents the Base64-decoded data of the input
*/
inline std::string decode(const std::string& input) { return decode(input.c_str(), input.size()); }
}
inline std::string base64_encode(const char* input, const unsigned long inputSize) { return base64::encode(input, inputSize); }
inline std::string base64_encode(const std::string& input) { return base64::encode(input.c_str(), input.size()); }

View file

@ -4,6 +4,12 @@
box-sizing: border-box;
}
.group_box {
border: lightgray solid 1px;
border-radius: 2px;
padding: 2px;
}
.contextMenu {
display: none;
z-index: 1000;
@ -42,10 +48,15 @@
margin-right: 4px;
}
.contextMenu li:hover {
.contextMenu li:hover:not(.disabled) {
background-color: #DEF;
}
.contextMenu li.disabled {
background-color: lightgray;
cursor: not-allowed;
}
.icon_loading {
border: 2px solid #f3f3f3; /* Light grey */
border-top: 2px solid #3498db; /* Blue */

View file

@ -1,20 +1,14 @@
<?php
$host = gethostname();
$localhost = false;
$testXF = true;
if($host == "WolverinDEV")
$localhost = true;
if(file_exists('auth.php'))
include_once('auth.php');
else if(file_exists('auth/auth.php'))
include_once('auth/auth.php');
else die("Could not resolve auth.php!");
if(!$localhost || $testXF){
if(file_exists('auth.php'))
include_once('auth.php');
else if(file_exists('auth/auth.php'))
include_once('auth/auth.php');
else die("Could not resolve auth.php!");
redirectOnInvalidSession();
} else {
function webPath() { return "auth/"; }
}
?>
@ -34,11 +28,13 @@
<x-properties id="properties">
<!-- <x-property key="" value=""/> -->
<?php
if($localhost) {
echo '<x-property key="connect_default_host" value="localhost"/>';
} else {
echo '<x-property key="connect_default_host" value="ts.TeaSpeak.de"/>';
function spawnProperty($name, $value) {
echo '<x-property key="' . $name . '" value="' . urlencode($value) . '"/>';
}
spawnProperty('connect_default_host', $localhost ? "localhost" : "ts.TeaSpeak.de");
spawnProperty('forum_user_data', $_COOKIE[$GLOBALS["COOKIE_NAME_USER_DATA"]]);
spawnProperty('forum_user_sign', $_COOKIE[$GLOBALS["COOKIE_NAME_USER_SIGN"]]);
?>
</x-properties>
@ -132,7 +128,7 @@
<footer>
<div class="container">
<div style="align-self: center;">TeaSpeak Web client by WolverinDEV</div>
<div style="align-self: center; position: fixed; right: 5px;"><a href="<?php echo webPath() . "auth.php?type=logout"; ?>">logout</a></div>
<div style="align-self: center; position: fixed; right: 5px;"><a href="<?php echo authPath() . "auth.php?type=logout"; ?>">logout</a></div>
</div>
</footer>
</html>

116
js/Identity.ts Normal file
View file

@ -0,0 +1,116 @@
enum IdentitifyType {
TEAFORO,
TEAMSPEAK
}
namespace TSIdentityHelper {
import Pointer_stringify = Module.Pointer_stringify;
export let funcationParseIdentity: any;
export let funcationParseIdentityByFile: any;
export let funcationCalculateSecurityLevel: any;
export let functionUid: any;
export let funcationExportIdentity: any;
export let funcationPublicKey: any;
export let funcationSignMessage: any;
let functionLastError: any;
let functionClearLastError: any;
let functionDestroyString: any;
let functionDestroyIdentity: any;
export function setup() : boolean {
functionDestroyString = Module.cwrap("destroy_string", "pointer", []);
functionLastError = Module.cwrap("last_error_message", null, ["string"]);
funcationParseIdentity = Module.cwrap("parse_identity", "pointer", ["string"]);
funcationParseIdentityByFile = Module.cwrap("parse_identity_file", "pointer", ["string"]);
functionDestroyIdentity = Module.cwrap("delete_identity", null, ["pointer"]);
funcationCalculateSecurityLevel = Module.cwrap("identity_security_level", "pointer", ["pointer"]);
funcationExportIdentity = Module.cwrap("identity_export", "pointer", ["pointer"]);
funcationPublicKey = Module.cwrap("identity_key_public", "pointer", ["pointer"]);
funcationSignMessage = Module.cwrap("identity_sign", "pointer", ["pointer", "string", "number"]);
functionUid = Module.cwrap("identity_uid", "pointer", ["pointer"]);
return Module.cwrap("tomcrypt_initialize", "number", [])() == 0;
}
export function last_error() : string {
return unwarpString(functionLastError());
}
export function unwarpString(str) : string {
if(str == "") return "";
let message: string = Pointer_stringify(str);
functionDestroyString(str);
return message;
}
export function loadIdentity(key: string) : TeamSpeakIdentity {
let handle = funcationParseIdentity(key);
if(!handle) return undefined;
return new TeamSpeakIdentity(handle, "TeaWeb user");
}
export function loadIdentityFromFileContains(contains: string) : TeamSpeakIdentity {
let handle = funcationParseIdentityByFile(contains);
if(!handle) return undefined;
return new TeamSpeakIdentity(handle, "TeaWeb user");
}
}
interface Identity {
name() : string;
uid() : string;
type() : IdentitifyType;
}
class TeamSpeakIdentity implements Identity {
private handle: any;
private _name: string;
constructor(handle: any, name: string) {
this.handle = handle;
this._name = name;
}
securityLevel() : number | undefined {
return parseInt(TSIdentityHelper.unwarpString(TSIdentityHelper.funcationCalculateSecurityLevel(this.handle)));
}
name() : string { return this._name; }
uid() : string {
return TSIdentityHelper.unwarpString(TSIdentityHelper.functionUid(this.handle));
}
type() : IdentitifyType { return IdentitifyType.TEAMSPEAK; }
signMessage(message: string): string {
return TSIdentityHelper.unwarpString(TSIdentityHelper.funcationSignMessage(this.handle, message, message.length));
}
exported() : string {
return TSIdentityHelper.unwarpString(TSIdentityHelper.funcationExportIdentity(this.handle));
}
publicKey() : string {
return TSIdentityHelper.unwarpString(TSIdentityHelper.funcationPublicKey(this.handle));
}
}
class TeaForumIdentity implements Identity {
readonly identityData: string;
readonly identityDataJson: string;
readonly identitySign: string;
constructor(data: string, sign: string) {
this.identityDataJson = data;
this.identityData = JSON.parse(this.identityDataJson);
this.identitySign = sign;
}
name() : string { return this.identityData["user_name"]; }
uid() : string { return "TeaForo#" + this.identityData["user_id"]; }
type() : IdentitifyType { return IdentitifyType.TEAFORO; }
}

View file

@ -86,7 +86,7 @@ class InfoBar {
else if (this._currentSelected instanceof ChannelEntry) {
let props = this._currentSelected.properties;
this._htmlTag.append(this.createInfoTable({
"Name": this._currentSelected.createChatTag(),
"Name": this._currentSelected.createChatTag().html(),
"Topic": this._currentSelected.properties.channel_topic,
"Codec": this._currentSelected.properties.channel_codec,
"Codec Quality": this._currentSelected.properties.channel_codec_quality,
@ -98,12 +98,20 @@ class InfoBar {
}
else if (this._currentSelected instanceof ClientEntry) {
this._currentSelected.updateVariables();
this._htmlTag.append(this.createInfoTable({
"Name": this._currentSelected.createChatTag(),
let version = this._currentSelected.properties.client_version;
if (!version)
version = "";
let infos = {
"Name": this._currentSelected.createChatTag().html(),
"Description": this._currentSelected.properties.client_description,
"Version": this._currentSelected.properties.client_version + " on " + this._currentSelected.properties.client_platform,
"Online since": "<a class='online'>" + formatDate(this._currentSelected.calculateOnlineTime()) + "</a>"
}));
"Version": "<a title='" + ChatMessage.formatMessage(version) + "'>" + version.split(" ")[0] + "</a>" + " on " + this._currentSelected.properties.client_platform,
"Online since": "<a class='online'>" + formatDate(this._currentSelected.calculateOnlineTime()) + "</a>",
"Volume": this._currentSelected.audioController.volume * 100 + " %"
};
if (this._currentSelected.properties["client_teaforum_id"] > 0) {
infos["TeaSpeak Account"] = "<a href='//forum.teaspeak.de/index.php?members/{1}/' target='_blank'>{0}</a>".format(this._currentSelected.properties["client_teaforum_name"], this._currentSelected.properties["client_teaforum_id"]);
}
this._htmlTag.append(this.createInfoTable(infos));
{
let serverGroups = $.spawn("div");
serverGroups

File diff suppressed because one or more lines are too long

View file

@ -106,7 +106,7 @@ class InfoBar {
} else if(this._currentSelected instanceof ChannelEntry) {
let props = this._currentSelected.properties;
this._htmlTag.append(this.createInfoTable({
"Name": this._currentSelected.createChatTag(),
"Name": this._currentSelected.createChatTag().html(),
"Topic": this._currentSelected.properties.channel_topic,
"Codec": this._currentSelected.properties.channel_codec,
"Codec Quality": this._currentSelected.properties.channel_codec_quality,
@ -117,12 +117,20 @@ class InfoBar {
}));
} else if(this._currentSelected instanceof ClientEntry) {
this._currentSelected.updateVariables();
this._htmlTag.append(this.createInfoTable({
"Name": this._currentSelected.createChatTag(),
let version: string = this._currentSelected.properties.client_version;
if(!version) version = "";
let infos = {
"Name": this._currentSelected.createChatTag().html(),
"Description": this._currentSelected.properties.client_description,
"Version": this._currentSelected.properties.client_version + " on " + this._currentSelected.properties.client_platform,
"Online since": "<a class='online'>" + formatDate(this._currentSelected.calculateOnlineTime()) + "</a>"
}));
"Version": "<a title='" + ChatMessage.formatMessage(version) + "'>" + version.split(" ")[0] + "</a>" + " on " + this._currentSelected.properties.client_platform,
"Online since": "<a class='online'>" + formatDate(this._currentSelected.calculateOnlineTime()) + "</a>",
"Volume": this._currentSelected.audioController.volume * 100 + " %"
};
if(this._currentSelected.properties["client_teaforum_id"] > 0) {
infos["TeaSpeak Account"] = "<a href='//forum.teaspeak.de/index.php?members/{1}/' target='_blank'>{0}</a>".format(this._currentSelected.properties["client_teaforum_name"], this._currentSelected.properties["client_teaforum_id"]);
}
this._htmlTag.append(this.createInfoTable(infos));
{
let serverGroups = $.spawn("div");

View file

@ -35,9 +35,16 @@ class ChatMessage {
return tag;
}
static formatMessage(message) {
return message
.replace(/ /g, '&nbsp;')
.replace(/\n/g, "<br/>");
/*
message = message
.replace(/ /g, '&nbsp;')
.replace(/\n/g, "<br/>");
*/
const div = document.createElement('div');
div.innerText = message;
message = div.innerHTML;
console.log(message + "->" + div.innerHTML);
return message;
}
}
class ChatEntry {
@ -49,12 +56,26 @@ class ChatEntry {
this.history = [];
this.onClose = function () { return true; };
}
static tagify(message, ...args) {
}
appendError(message, ...args) {
this.appendMessage("<a style='color: red'>{0}</a>".format(ChatMessage.formatMessage(message).format(...args)), false);
}
appendMessage(message, fmt = true, ...args) {
let parms = [];
for (let index = 2; index < arguments.length; index++) {
if (typeof arguments[index] == "string")
arguments[index] = ChatMessage.formatMessage(arguments[index]);
else if (arguments[index] instanceof jQuery)
arguments[index] = arguments[index].html();
else {
console.error("Invalid type " + typeof arguments[index] + "|" + arguments[index].prototype);
arguments[index] = arguments[index].toString();
}
parms.push(arguments[index]);
}
let msg = fmt ? ChatMessage.formatMessage(message) : message;
msg = msg.format(...args);
msg = msg.format(parms);
let elm = new ChatMessage(msg);
this.history.push(elm);
while (this.history.length > 100) {
@ -91,7 +112,7 @@ class ChatEntry {
get htmlTag() {
if (this._htmlTag)
return this._htmlTag;
var tag = $.spawn("div");
let tag = $.spawn("div");
tag.addClass("chat");
tag.append("<div class=\"chatIcon icon clicon " + this.chatIcon() + "\"></div>");
tag.append("<a class='name'>" + this._name + "</a>");

File diff suppressed because one or more lines are too long

View file

@ -46,9 +46,16 @@ class ChatMessage {
}
static formatMessage(message: string) : string {
return message
.replace(/ /g, '&nbsp;')
.replace(/\n/g, "<br/>");
/*
message = message
.replace(/ /g, '&nbsp;')
.replace(/\n/g, "<br/>");
*/
const div = document.createElement('div');
div.innerText = message;
message = div.innerHTML;
console.log(message + "->" + div.innerHTML);
return message;
}
}
@ -75,13 +82,27 @@ class ChatEntry {
this.onClose = function () { return true; }
}
static tagify(message: string, ...args) {
}
appendError(message: string, ...args) {
this.appendMessage("<a style='color: red'>{0}</a>".format(ChatMessage.formatMessage(message).format(...args)), false);
}
appendMessage(message : string, fmt: boolean = true, ...args) {
let parms: any[] = [];
for(let index = 2; index < arguments.length; index++) {
if(typeof arguments[index] == "string") arguments[index] = ChatMessage.formatMessage(arguments[index]);
else if(arguments[index] instanceof jQuery) arguments[index] = arguments[index].html();
else {
console.error("Invalid type " + typeof arguments[index] + "|" + arguments[index].prototype);
arguments[index] = arguments[index].toString();
}
parms.push(arguments[index]);
}
let msg : string = fmt ? ChatMessage.formatMessage(message) : message;
msg = msg.format(...args);
msg = msg.format(parms);
let elm = new ChatMessage(msg);
this.history.push(elm);
while(this.history.length > 100) {
@ -119,7 +140,7 @@ class ChatEntry {
get htmlTag() {
if(this._htmlTag) return this._htmlTag;
var tag = $.spawn("div");
let tag = $.spawn("div");
tag.addClass("chat");
tag.append("<div class=\"chatIcon icon clicon " + this.chatIcon() + "\"></div>");

View file

@ -60,9 +60,8 @@ class TSClient {
}
setup() {
this.controlBar.initialise();
this.serverConnection.on_connected = this.onConnected.bind(this);
}
startConnection(addr) {
startConnection(addr, identity, name) {
if (this.serverConnection)
this.handleDisconnect(DisconnectReason.REQUESTED);
let idx = addr.lastIndexOf(':');
@ -78,7 +77,7 @@ class TSClient {
}
console.log("Start connection to " + host + ":" + port);
this.channelTree.initialiseHead(addr);
this.serverConnection.startConnection(host, port);
this.serverConnection.startConnection(host, port, new HandshakeHandler(identity, name));
}
getClient() { return this._ownEntry; }
getClientId() { return this._clientId; } //TODO here
@ -97,7 +96,6 @@ class TSClient {
console.log("Client connected!");
this.channelTree.registerClient(this._ownEntry);
this.settings.loadServer();
chat.serverChat().appendMessage("Connected");
this.serverConnection.sendCommand("channelsubscribeall");
this.permissions.requestPermissionList();
if (this.groups.serverGroups.length == 0)

View file

@ -1 +1 @@
{"version":3,"file":"client.js","sourceRoot":"","sources":["client.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"client.js","sourceRoot":"","sources":["client.ts"],"names":[],"mappings":"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"}

View file

@ -74,11 +74,9 @@ class TSClient {
setup() {
this.controlBar.initialise();
this.serverConnection.on_connected = this.onConnected.bind(this);
}
startConnection(addr: string) {
startConnection(addr: string, identity: Identity, name?: string) {
if(this.serverConnection)
this.handleDisconnect(DisconnectReason.REQUESTED);
@ -95,7 +93,7 @@ class TSClient {
}
console.log("Start connection to " + host + ":" + port);
this.channelTree.initialiseHead(addr);
this.serverConnection.startConnection(host, port);
this.serverConnection.startConnection(host, port, new HandshakeHandler(identity, name));
}
@ -121,7 +119,6 @@ class TSClient {
console.log("Client connected!");
this.channelTree.registerClient(this._ownEntry);
this.settings.loadServer();
chat.serverChat().appendMessage("Connected");
this.serverConnection.sendCommand("channelsubscribeall");
this.permissions.requestPermissionList();
if(this.groups.serverGroups.length == 0)

63
js/codec/BasicCodec.ts Normal file
View file

@ -0,0 +1,63 @@
/// <reference path="Codec.ts"/>
abstract class BasicCodec implements Codec {
protected _audioContext: OfflineAudioContext;
protected _decodeResampler: AudioResampler;
protected _encodeResampler: AudioResampler;
protected _codecSampleRate: number;
on_encoded_data: (Uint8Array) => void = $ => {};
channelCount: number = 1;
samplesPerUnit: number = 960;
constructor(codecSampleRate: number) {
this.channelCount = 1;
this.samplesPerUnit = 960;
this._audioContext = new OfflineAudioContext(1, 1024,44100 );
this._codecSampleRate = codecSampleRate;
this._decodeResampler = new AudioResampler();
this._encodeResampler = new AudioResampler(codecSampleRate);
}
abstract name() : string;
abstract initialise();
abstract deinitialise();
abstract reset() : boolean;
protected abstract decode(data: Uint8Array) : Promise<AudioBuffer>;
protected abstract encode(data: AudioBuffer) : Promise<Uint8Array | string>;
encodeSamples(cache: CodecClientCache, pcm: AudioBuffer) {
this._encodeResampler.resample(pcm).then(buffer => this.encodeSamples0(cache, buffer))
.catch(error => console.error("Could not resample PCM data for codec. Error:" + error));
}
private encodeSamples0(cache: CodecClientCache, buffer: AudioBuffer) {
cache._chunks.push(new BufferChunk(buffer)); //TODO multi channel!
while(cache.bufferedSamples(this.samplesPerUnit) >= this.samplesPerUnit) {
let buffer = this._audioContext.createBuffer(this.channelCount, this.samplesPerUnit, this._codecSampleRate);
let index = 0;
while(index < this.samplesPerUnit) {
let buf = cache._chunks[0];
let cpyBytes = buf.copyRangeTo(buffer, this.samplesPerUnit - index, index);
index += cpyBytes;
buf.index += cpyBytes;
if(buf.index == buf.buffer.length)
cache._chunks.pop_front();
}
this.encode(buffer).then(result => {
if(result instanceof Uint8Array) this.on_encoded_data(result);
else console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
});
}
return true;
}
decodeSamples(cache: CodecClientCache, data: Uint8Array) : Promise<AudioBuffer> {
return this.decode(data).then(buffer => this._decodeResampler.resample(buffer));
}
}

View file

@ -1,12 +1,3 @@
/// <reference path="../voice/AudioResampler.ts" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class BufferChunk {
constructor(buffer) {
this.buffer = buffer;
@ -20,15 +11,9 @@ class BufferChunk {
return copy;
}
}
class Codec {
constructor(codecSampleRate) {
this.on_encoded_data = ($) => { };
class CodecClientCache {
constructor() {
this._chunks = [];
this.channelCount = 1;
this.samplesPerUnit = 960;
this._codecSampleRate = codecSampleRate;
this._decodeResampler = new AudioResampler();
this._encodeResampler = new AudioResampler(codecSampleRate);
}
bufferedSamples(max = 0) {
let value = 0;
@ -36,126 +21,5 @@ class Codec {
value += this._chunks[i].buffer.length - this._chunks[i].index;
return value;
}
encodeSamples(pcm) {
return __awaiter(this, void 0, void 0, function* () {
this._encodeResampler.resample(pcm).then(buffer => this.encodeSamples0(buffer))
.catch(error => console.error("Could not resample PCM data for codec. Error:" + error));
});
}
encodeSamples0(buffer) {
this._chunks.push(new BufferChunk(buffer)); //TODO multi channel!
while (this.bufferedSamples(this.samplesPerUnit) >= this.samplesPerUnit) {
let buffer = AudioController.globalContext.createBuffer(this.channelCount, this.samplesPerUnit, this._codecSampleRate);
let index = 0;
while (index < this.samplesPerUnit) {
let buf = this._chunks[0];
let cpyBytes = buf.copyRangeTo(buffer, this.samplesPerUnit - index, index);
index += cpyBytes;
buf.index += cpyBytes;
if (buf.index == buf.buffer.length)
this._chunks.pop_front();
}
let result = this.encode(buffer);
if (result instanceof Uint8Array)
this.on_encoded_data(result);
else
console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
}
return true;
}
decodeSamples(data) {
return this.decode(data).then(buffer => this._decodeResampler.resample(buffer));
}
}
class RawCodec extends Codec {
constructor(codecSampleRate) {
super(codecSampleRate);
this.bufferSize = 4096 * 4;
}
name() {
return "raw";
}
initialise() {
this.converterRaw = Module._malloc(this.bufferSize);
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
}
deinitialise() { }
decode(data) {
return new Promise((resolve, reject) => {
this.converter.set(data);
let buf = Module.HEAPF32.slice(this.converter.byteOffset / 4, (this.converter.byteOffset / 4) + data.length / 4);
let audioBuf = AudioController.globalContext.createBuffer(1, data.length / 4, this._codecSampleRate);
audioBuf.copyToChannel(buf, 0);
resolve(audioBuf);
});
}
encode(data) {
return new Uint8Array(data.getChannelData(0));
}
}
var OpusType;
(function (OpusType) {
OpusType[OpusType["VOIP"] = 2048] = "VOIP";
OpusType[OpusType["AUDIO"] = 2049] = "AUDIO";
OpusType[OpusType["RESTRICTED_LOWDELAY"] = 2051] = "RESTRICTED_LOWDELAY";
})(OpusType || (OpusType = {}));
class OpusCodec extends Codec {
constructor(channelCount, type) {
super(48000);
this.bufferSize = 4096 * 2;
this.channelCount = channelCount;
this.type = type;
}
name() {
return "Opus (Type: " + OpusCodec[this.type] + " Channels: " + this.channelCount + ")";
}
initialise() {
this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "pointer", ["number", "number"]);
this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["pointer", "pointer", "number", "number"]);
/* codec_opus_decode(handle, buffer, length, maxlength) */
this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["pointer", "pointer", "number", "number"]);
this.nativeHandle = this.fn_newHandle(this.channelCount, this.type);
this.encodeBufferRaw = Module._malloc(this.bufferSize);
this.encodeBuffer = new Float32Array(Module.HEAPF32.buffer, this.encodeBufferRaw, this.bufferSize / 4);
this.decodeBufferRaw = Module._malloc(this.bufferSize);
this.decodeBuffer = new Uint8Array(Module.HEAPU8.buffer, this.decodeBufferRaw, this.bufferSize);
}
deinitialise() { } //TODO
decode(data) {
return new Promise((resolve, reject) => {
if (data.byteLength > this.decodeBuffer.byteLength)
throw "Data to long!";
this.decodeBuffer.set(data);
//console.log("decode(" + data.length + ")");
//console.log(data);
let result = this.fn_decode(this.nativeHandle, this.decodeBuffer.byteOffset, data.byteLength, this.decodeBuffer.byteLength);
if (result < 0) {
reject("invalid result on decode (" + result + ")");
return;
}
//console.log("decoded: " + result);
let buf = Module.HEAPF32.slice(this.decodeBuffer.byteOffset / 4, (this.decodeBuffer.byteOffset / 4) + (result * this.channelCount));
let audioBuf = AudioController.globalContext.createBuffer(this.channelCount, result, this._codecSampleRate);
for (let offset = 0; offset < result; offset++) {
for (let channel = 0; channel < this.channelCount; channel++)
audioBuf.getChannelData(channel)[offset] = buf[offset * this.channelCount + channel];
}
resolve(audioBuf);
});
}
encode(data) {
if (data.length * this.channelCount > this.encodeBuffer.length)
throw "Data to long!";
for (let offset = 0; offset < data.length; offset++) {
for (let channel = 0; channel < this.channelCount; channel++)
this.encodeBuffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset];
}
let result = this.fn_encode(this.nativeHandle, this.encodeBuffer.byteOffset, data.length, this.encodeBuffer.byteLength);
if (result < 0) {
return "invalid result on encode (" + result + ")";
}
let buf = Module.HEAP8.slice(this.encodeBuffer.byteOffset, this.encodeBuffer.byteOffset + result);
return Uint8Array.from(buf);
}
}
//# sourceMappingURL=Codec.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,6 @@
/// <reference path="../voice/AudioResampler.ts" />
interface CodecCostructor {
new (codecSampleRate: number) : Codec;
}
class BufferChunk {
buffer: AudioBuffer;
@ -21,191 +23,29 @@ class BufferChunk {
}
}
abstract class Codec {
on_encoded_data: (Uint8Array) => void = ($) => {};
class CodecClientCache {
_chunks: BufferChunk[] = [];
protected _decodeResampler: AudioResampler;
protected _encodeResampler: AudioResampler;
protected _codecSampleRate: number;
protected _chunks: BufferChunk[] = [];
channelCount: number = 1;
samplesPerUnit: number = 960;
protected constructor(codecSampleRate: number){
this._codecSampleRate = codecSampleRate;
this._decodeResampler = new AudioResampler();
this._encodeResampler = new AudioResampler(codecSampleRate);
}
abstract name() : string;
abstract initialise();
abstract deinitialise();
protected abstract decode(data: Uint8Array) : Promise<AudioBuffer>;
protected abstract encode(data: AudioBuffer) : Uint8Array | string;
protected bufferedSamples(max: number = 0) : number {
bufferedSamples(max: number = 0) : number {
let value = 0;
for(let i = 0; i < this._chunks.length && value < max; i++)
value += this._chunks[i].buffer.length - this._chunks[i].index;
return value;
}
async encodeSamples(pcm: AudioBuffer) {
this._encodeResampler.resample(pcm).then(buffer => this.encodeSamples0(buffer))
.catch(error => console.error("Could not resample PCM data for codec. Error:" + error));
}
private encodeSamples0(buffer: AudioBuffer) {
this._chunks.push(new BufferChunk(buffer)); //TODO multi channel!
while(this.bufferedSamples(this.samplesPerUnit) >= this.samplesPerUnit) {
let buffer = AudioController.globalContext.createBuffer(this.channelCount, this.samplesPerUnit, this._codecSampleRate);
let index = 0;
while(index < this.samplesPerUnit) {
let buf = this._chunks[0];
let cpyBytes = buf.copyRangeTo(buffer, this.samplesPerUnit - index, index);
index += cpyBytes;
buf.index += cpyBytes;
if(buf.index == buf.buffer.length)
this._chunks.pop_front();
}
let result = this.encode(buffer);
if(result instanceof Uint8Array) this.on_encoded_data(result);
else console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
}
return true;
}
decodeSamples(data: Uint8Array) : Promise<AudioBuffer> {
return this.decode(data).then(buffer => this._decodeResampler.resample(buffer));
}
}
class RawCodec extends Codec {
converterRaw: any;
converter: Uint8Array;
bufferSize: number = 4096 * 4;
interface Codec {
on_encoded_data: (Uint8Array) => void;
constructor(codecSampleRate: number){
super(codecSampleRate);
}
channelCount: number;
samplesPerUnit: number;
name(): string {
return "raw";
}
name() : string;
initialise();
deinitialise();
initialise() {
this.converterRaw = Module._malloc(this.bufferSize);
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
}
decodeSamples(cache: CodecClientCache, data: Uint8Array) : Promise<AudioBuffer>;
encodeSamples(cache: CodecClientCache, pcm: AudioBuffer);
deinitialise() { }
protected decode(data: Uint8Array): Promise<AudioBuffer> {
return new Promise<AudioBuffer>((resolve, reject) => {
this.converter.set(data);
let buf = Module.HEAPF32.slice(this.converter.byteOffset / 4, (this.converter.byteOffset / 4) + data.length / 4);
let audioBuf = AudioController.globalContext.createBuffer(1, data.length / 4, this._codecSampleRate);
audioBuf.copyToChannel(buf, 0);
resolve(audioBuf);
});
}
protected encode(data: AudioBuffer): Uint8Array | string {
return new Uint8Array(data.getChannelData(0));
}
}
enum OpusType {
VOIP = 2048,
AUDIO = 2049,
RESTRICTED_LOWDELAY = 2051
}
class OpusCodec extends Codec {
private nativeHandle: any;
private type: OpusType;
private fn_newHandle: any;
private fn_decode: any;
private fn_encode: any;
private bufferSize = 4096 * 2;
private encodeBufferRaw: any;
private encodeBuffer: Float32Array;
private decodeBufferRaw: any;
private decodeBuffer: Uint8Array;
constructor(channelCount: number, type: OpusType) {
super(48000);
this.channelCount = channelCount;
this.type = type;
}
name(): string {
return "Opus (Type: " + OpusCodec[this.type] + " Channels: " + this.channelCount + ")";
}
initialise() {
this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "pointer", ["number", "number"]);
this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["pointer", "pointer", "number", "number"]);
/* codec_opus_decode(handle, buffer, length, maxlength) */
this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["pointer", "pointer", "number", "number"]);
this.nativeHandle = this.fn_newHandle(this.channelCount, this.type);
this.encodeBufferRaw = Module._malloc(this.bufferSize);
this.encodeBuffer = new Float32Array(Module.HEAPF32.buffer, this.encodeBufferRaw, this.bufferSize / 4);
this.decodeBufferRaw = Module._malloc(this.bufferSize);
this.decodeBuffer = new Uint8Array(Module.HEAPU8.buffer, this.decodeBufferRaw, this.bufferSize);
}
deinitialise() { } //TODO
decode(data: Uint8Array): Promise<AudioBuffer> {
return new Promise<AudioBuffer>((resolve, reject) => {
if (data.byteLength > this.decodeBuffer.byteLength) throw "Data to long!";
this.decodeBuffer.set(data);
//console.log("decode(" + data.length + ")");
//console.log(data);
let result = this.fn_decode(this.nativeHandle, this.decodeBuffer.byteOffset, data.byteLength, this.decodeBuffer.byteLength);
if (result < 0) {
reject("invalid result on decode (" + result + ")");
return;
}
//console.log("decoded: " + result);
let buf = Module.HEAPF32.slice(this.decodeBuffer.byteOffset / 4, (this.decodeBuffer.byteOffset / 4) + (result * this.channelCount));
let audioBuf = AudioController.globalContext.createBuffer(this.channelCount, result, this._codecSampleRate);
for (let offset = 0; offset < result; offset++) {
for (let channel = 0; channel < this.channelCount; channel++)
audioBuf.getChannelData(channel)[offset] = buf[offset * this.channelCount + channel];
}
resolve(audioBuf);
});
}
encode(data: AudioBuffer): Uint8Array | string {
if (data.length * this.channelCount > this.encodeBuffer.length) throw "Data to long!";
for (let offset = 0; offset < data.length; offset++) {
for (let channel = 0; channel < this.channelCount; channel++)
this.encodeBuffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset];
}
let result = this.fn_encode(this.nativeHandle, this.encodeBuffer.byteOffset, data.length, this.encodeBuffer.byteLength);
if (result < 0) {
return "invalid result on encode (" + result + ")";
}
let buf = Module.HEAP8.slice(this.encodeBuffer.byteOffset, this.encodeBuffer.byteOffset + result);
return Uint8Array.from(buf);
}
reset() : boolean;
}

145
js/codec/CodecWrapper.ts Normal file
View file

@ -0,0 +1,145 @@
/// <reference path="BasicCodec.ts"/>
enum CodecWorkerType {
WORKER_OPUS
}
class CodecWrapper extends BasicCodec {
private _worker: Worker;
private _workerListener: {token: string, resolve: (data: any) => void}[] = [];
private _workerCallbackToken = "callback_token";
private _workerTokeIndex: number = 0;
type: CodecWorkerType;
name(): string {
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
}
initialise() {
this.spawnWorker();
this.sendWorkerMessage({
command: "initialise",
type: this.type,
channelCount: this.channelCount
});
}
deinitialise() {
this.sendWorkerMessage({
command: "deinitialise"
});
}
decode(data: Uint8Array): Promise<AudioBuffer> {
let token = this._workerTokeIndex++ + "_token";
let result = new Promise<AudioBuffer>((resolve, reject) => {
this._workerListener.push(
{
token: token,
resolve: (data) => {
if(data.success) {
let array = new Float32Array(data.dataLength);
for(let index = 0; index < array.length; index++)
array[index] = data.data[index];
let audioBuf = this._audioContext.createBuffer(this.channelCount, array.length / this.channelCount, this._codecSampleRate);
for (let channel = 0; channel < this.channelCount; channel++)
for (let offset = 0; offset < audioBuf.length; offset++)
audioBuf.getChannelData(channel)[offset] = array[channel * audioBuf.length + offset];
resolve(audioBuf);
} else {
reject(data.message);
}
}
}
);
});
this.sendWorkerMessage({
command: "decodeSamples",
token: token,
data: data,
dataLength: data.length
});
return result;
}
encode(data: AudioBuffer) : Promise<Uint8Array> {
let token = this._workerTokeIndex++ + "_token";
let result = new Promise<Uint8Array>((resolve, reject) => {
this._workerListener.push(
{
token: token,
resolve: (data) => {
if(data.success) {
let array = new Uint8Array(data.dataLength);
for(let index = 0; index < array.length; index++)
array[index] = data.data[index];
resolve(array);
} else {
reject(data.message);
}
}
}
);
});
let buffer = new Float32Array(this.channelCount * data.length);
for (let offset = 0; offset < data.length; offset++) {
for (let channel = 0; channel < this.channelCount; channel++)
buffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset];
}
//FIXME test if this is right!
this.sendWorkerMessage({
command: "encodeSamples",
token: token,
data: buffer,
dataLength: buffer.length
});
return result;
}
reset() : boolean {
this.sendWorkerMessage({
command: "reset"
});
return true;
}
constructor(type: CodecWorkerType, channelCount: number) {
super(48000);
this.type = type;
this.channelCount = channelCount;
}
private sendWorkerMessage(message: any, transfare?: any[]) {
this._worker.postMessage(JSON.stringify(message), transfare);
}
private onWorkerMessage(message: any) {
if(!message["token"]) {
console.error("Invalid worker token!");
return;
}
if(message["token"] == this._workerCallbackToken) {
console.log("Callback data!");
return;
}
for(let entry of this._workerListener) {
if(entry.token == message["token"]) {
entry.resolve(message);
this._workerListener.remove(entry);
return;
}
}
console.error("Could not find worker token entry! (" + message["token"] + ")");
}
private spawnWorker() {
this._worker = new Worker("js/codec/CompiledCodecWorker.js");
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
}
}

38
js/codec/RawCodec.ts Normal file
View file

@ -0,0 +1,38 @@
/// <reference path="BasicCodec.ts"/>
class RawCodec extends BasicCodec {
converterRaw: any;
converter: Uint8Array;
bufferSize: number = 4096 * 4;
constructor(codecSampleRate: number){
super(codecSampleRate);
}
name(): string {
return "raw";
}
initialise() {
this.converterRaw = Module._malloc(this.bufferSize);
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
}
deinitialise() { }
protected decode(data: Uint8Array): Promise<AudioBuffer> {
return new Promise<AudioBuffer>((resolve, reject) => {
this.converter.set(data);
let buf = Module.HEAPF32.slice(this.converter.byteOffset / 4, (this.converter.byteOffset / 4) + data.length / 4);
let audioBuf = this._audioContext.createBuffer(1, data.length / 4, this._codecSampleRate);
audioBuf.copyToChannel(buf, 0);
resolve(audioBuf);
});
}
protected encode(data: AudioBuffer): Promise<Uint8Array> {
return new Promise<Uint8Array>(resolve => resolve(new Uint8Array(data.getChannelData(0))));
}
reset() : boolean { return true; }
}

17
js/codec/tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "none",
"target": "es6",
"sourceMap": true,
"outFile": "CompiledCodecWorker.js"
},
"files": [
"workers/CodecWorker.ts",
"workers/CodecWorker.ts",
"workers/OpusCodec.ts",
"../proto.ts"
],
"exclude": [
"node_modules"
]
}

View file

@ -0,0 +1,88 @@
const prefix = "[CodecWorker] ";
interface CodecWorker {
name();
initialise();
deinitialise();
decode(data: Uint8Array);
encode(data: Float32Array) : Uint8Array | string;
reset();
}
enum CodecWorkerType {
WORKER_OPUS
}
let codecInstance: CodecWorker;
onmessage = function(e) {
let data = JSON.parse(e.data);
//console.log(data);
let res: any = {};
res.token = data.token;
switch (data.command) {
case "initialise":
console.log(prefix + "Got initialize for type " + CodecWorkerType[data.type as CodecWorkerType]);
switch (data.type as CodecWorkerType) {
case CodecWorkerType.WORKER_OPUS:
codecInstance = new OpusWorker(data.channelCount, data.channelCount == 1 ? OpusType.VOIP : OpusType.AUDIO);
break;
default:
console.error("Could not resolve opus type!");
return;
}
codecInstance.initialise();
break;
case "encodeSamples":
let encodeArray = new Float32Array(data.dataLength);
for(let index = 0; index < encodeArray.length; index++)
encodeArray[index] = data.data[index];
let encodeResult = codecInstance.encode(encodeArray);
if(typeof encodeResult === "string") {
res.success = false;
res.message = encodeResult;
} else {
res.success = true;
res.data = encodeResult;
res.dataLength = encodeResult.length;
}
sendMessage(res, e.origin);
break;
case "decodeSamples":
let decodeArray = new Uint8Array(data.dataLength);
for(let index = 0; index < decodeArray.length; index++)
decodeArray[index] = data.data[index];
let decodeResult = codecInstance.decode(decodeArray);
if(typeof decodeResult === "string") {
res.success = false;
res.message = decodeResult;
} else {
res.success = true;
res.data = decodeResult;
res.dataLength = decodeResult.length;
}
sendMessage(res, e.origin);
break;
case "reset":
codecInstance.reset();
break;
default:
console.error(prefix + "Unknown type " + data.command);
}
};
declare function postMessage(message: any): void;
function sendMessage(message: any, origin: string){
postMessage(JSON.stringify(message));
}

View file

@ -0,0 +1,90 @@
/// <reference path="CodecWorker.ts" />
//"js/TeaWeb-Native.js", "asm/generated/TeaWeb-Native.js"
try {
importScripts("../TeaWeb-Native.js");
} catch (e) {
try {
importScripts("../../asm/generated/TeaWeb-Native.js");
} catch (e) {
console.error("Could not load native script!");
console.log(e);
}
}
enum OpusType {
VOIP = 2048,
AUDIO = 2049,
RESTRICTED_LOWDELAY = 2051
}
class OpusWorker implements CodecWorker {
private channelCount: number;
private nativeHandle: any;
private type: OpusType;
private fn_newHandle: any;
private fn_decode: any;
private fn_encode: any;
private fn_reset: any;
private bufferSize = 4096 * 2;
private encodeBufferRaw: any;
private encodeBuffer: Float32Array;
private decodeBufferRaw: any;
private decodeBuffer: Uint8Array;
constructor(channelCount: number, type: OpusType) {
this.channelCount = channelCount;
this.type = type;
}
name(): string {
return "Opus (Type: " + OpusWorker[this.type] + " Channels: " + this.channelCount + ")";
}
initialise() {
this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "pointer", ["number", "number"]);
this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["pointer", "pointer", "number", "number"]);
/* codec_opus_decode(handle, buffer, length, maxlength) */
this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["pointer", "pointer", "number", "number"]);
this.fn_reset = Module.cwrap("codec_opus_reset", "number", ["pointer"]);
this.nativeHandle = this.fn_newHandle(this.channelCount, this.type);
this.encodeBufferRaw = Module._malloc(this.bufferSize);
this.encodeBuffer = new Float32Array(Module.HEAPF32.buffer, this.encodeBufferRaw, this.bufferSize / 4);
this.decodeBufferRaw = Module._malloc(this.bufferSize);
this.decodeBuffer = new Uint8Array(Module.HEAPU8.buffer, this.decodeBufferRaw, this.bufferSize);
}
deinitialise() { } //TODO
decode(data: Uint8Array): Float32Array | string {
if (data.byteLength > this.decodeBuffer.byteLength) return "Data to long!";
this.decodeBuffer.set(data);
//console.log("decode(" + data.length + ")");
//console.log(data);
let result = this.fn_decode(this.nativeHandle, this.decodeBuffer.byteOffset, data.byteLength, this.decodeBuffer.byteLength);
if (result < 0) {
return "invalid result on decode (" + result + ")";
}
return Module.HEAPF32.slice(this.decodeBuffer.byteOffset / 4, (this.decodeBuffer.byteOffset / 4) + (result * this.channelCount));
}
encode(data: Float32Array): Uint8Array | string {
this.encodeBuffer.set(data);
let result = this.fn_encode(this.nativeHandle, this.encodeBuffer.byteOffset, data.length, this.encodeBuffer.byteLength);
if (result < 0) {
return "invalid result on encode (" + result + ")";
}
let buf = Module.HEAP8.slice(this.encodeBuffer.byteOffset, this.encodeBuffer.byteOffset + result);
return Uint8Array.from(buf);
}
reset() {
this.fn_reset(this.nativeHandle);
}
}

View file

@ -18,10 +18,10 @@ class ServerConnection {
this._connectionState = ConnectionState.UNCONNECTED;
this._connectTimeoutHandler = undefined;
this.on_connect = () => {
console.log("Client connected!");
chat.serverChat().appendMessage("Client connected");
console.log("Socket connected");
chat.serverChat().appendMessage("Logging in...");
this._handshakeHandler.startHandshake();
};
this.on_connected = () => { };
this._client = client;
this._socket = null;
this.commandHandler = new ConnectionCommandHandler(this);
@ -31,7 +31,7 @@ class ServerConnection {
generateReturnCode() {
return (this._retCodeIdx++).toString();
}
startConnection(host, port, timeout = 1000) {
startConnection(host, port, handshake, timeout = 1000) {
if (this._connectTimeoutHandler) {
clearTimeout(this._connectTimeoutHandler);
this._connectTimeoutHandler = null;
@ -40,23 +40,28 @@ class ServerConnection {
this.updateConnectionState(ConnectionState.CONNECTING);
this._remoteHost = host;
this._remotePort = port;
this._handshakeHandler = handshake;
this._handshakeHandler.setConnection(this);
chat.serverChat().appendMessage("Connecting to " + host + ":" + port);
const self = this;
try {
this._connectTimeoutHandler = setTimeout(() => {
this.disconnect();
this._client.handleDisconnect(DisconnectReason.CONNECT_FAILURE);
}, timeout);
this._socket = new WebSocket('wss:' + this._remoteHost + ":" + this._remotePort);
let sockCpy;
this._socket = (sockCpy = new WebSocket('wss:' + this._remoteHost + ":" + this._remotePort));
clearTimeout(this._connectTimeoutHandler);
this._connectTimeoutHandler = null;
const _socketCpy = this._socket;
if (this._socket != sockCpy)
return; //Connect timeouted
this._socket.onopen = () => {
if (this._socket != _socketCpy)
if (this._socket != sockCpy)
return;
this.on_connect();
};
this._socket.onclose = event => {
if (this._socket != _socketCpy)
if (this._socket != sockCpy)
return;
this._client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
code: event.code,
@ -65,13 +70,13 @@ class ServerConnection {
});
};
this._socket.onerror = e => {
if (this._socket != _socketCpy)
if (this._socket != sockCpy)
return;
console.log("Got error: (" + self._socket.readyState + ")");
console.log(e);
};
this._socket.onmessage = msg => {
if (this._socket != _socketCpy)
if (this._socket != sockCpy)
return;
self.handleWebSocketMessage(msg.data);
};
@ -203,6 +208,53 @@ class ServerConnection {
return this.sendCommand("clientupdate", data);
}
}
class HandshakeHandler {
constructor(identity, name) {
this.identity = identity;
this.name = name;
}
setConnection(con) {
this.connection = con;
this.connection.commandHandler["handshakeidentityproof"] = this.handleCommandHandshakeIdentityProof.bind(this);
}
startHandshake() {
let data = {
intention: 0,
authentication_method: this.identity.type()
};
if (this.identity.type() == IdentitifyType.TEAMSPEAK) {
data.publicKey = this.identity.publicKey();
}
else if (this.identity.type() == IdentitifyType.TEAFORO) {
data.data = this.identity.identityDataJson;
}
this.connection.sendCommand("handshakebegin", data).catch(error => {
console.log(error);
//TODO here
});
}
handleCommandHandshakeIdentityProof(json) {
let proof;
if (this.identity.type() == IdentitifyType.TEAMSPEAK) {
proof = this.identity.signMessage(json[0]["message"]);
}
else if (this.identity.type() == IdentitifyType.TEAFORO) {
proof = this.identity.identitySign;
}
this.connection.sendCommand("handshakeindentityproof", { proof: proof }).then(() => {
this.connection.sendCommand("clientinit", {
//TODO variables!
client_nickname: this.name ? this.name : this.identity.name(),
client_platform: navigator.platform,
client_version: navigator.userAgent,
client_browser_engine: navigator.product
});
}).catch(error => {
console.error("Got login error");
console.log(error);
}); //TODO handle error
}
}
class ConnectionCommandHandler {
constructor(connection) {
this.connection = connection;
@ -257,7 +309,7 @@ class ConnectionCommandHandler {
}
chat.serverChat().name = this.connection._client.channelTree.server.properties["virtualserver_name"];
chat.serverChat().appendMessage("Connected as {0}", true, this.connection._client.getClient().createChatTag(true));
this.connection.on_connected();
globalClient.onConnected();
}
createChannelFromJson(json, ignoreOrder = false) {
let tree = this.connection._client.channelTree;
@ -483,14 +535,18 @@ class ConnectionCommandHandler {
json = json[0]; //Only one bulk
//TODO chat format?
let mode = json["targetmode"];
let invoker = this.connection._client.channelTree.findClient(json["invokerid"]);
if (!invoker) {
console.error("Invalid chat message!");
return;
}
if (mode == 1) {
let invoker = this.connection._client.channelTree.findClient(json["invokerid"]);
let target = this.connection._client.channelTree.findClient(json["target"]);
if (!invoker) {
console.error("Got private message from invalid client!");
return;
}
if (!target) {
console.error("Got private message from invalid client!");
return;
}
if (invoker == this.connection._client.getClient()) {
let target = this.connection._client.channelTree.findClient(json["target"]);
target.chat(true).appendMessage("<< " + json["msg"]);
}
else {
@ -498,10 +554,10 @@ class ConnectionCommandHandler {
}
}
else if (mode == 2) {
chat.channelChat().appendMessage("{0} >> " + json["msg"], true, invoker.createChatTag(true));
chat.channelChat().appendMessage("{0} >> {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"]);
}
else if (mode == 3) {
chat.serverChat().appendMessage("{0} >> " + json["msg"], true, invoker.createChatTag(true));
chat.serverChat().appendMessage("{0} >> {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"]);
}
}
handleNotifyClientUpdated(json) {

File diff suppressed because one or more lines are too long

View file

@ -35,6 +35,7 @@ class ServerConnection {
_remotePort: number;
_socket: WebSocket;
_connectionState: ConnectionState = ConnectionState.UNCONNECTED;
_handshakeHandler: HandshakeHandler;
commandHandler: ConnectionCommandHandler;
private _connectTimeoutHandler: NodeJS.Timer = undefined;
@ -52,17 +53,16 @@ class ServerConnection {
}
on_connect: () => void = () => {
console.log("Client connected!");
chat.serverChat().appendMessage("Client connected");
console.log("Socket connected");
chat.serverChat().appendMessage("Logging in...");
this._handshakeHandler.startHandshake();
};
on_connected: () => void = () => {};
private generateReturnCode() : string {
return (this._retCodeIdx++).toString();
}
startConnection(host : string, port : number, timeout: number = 1000) {
startConnection(host : string, port : number, handshake: HandshakeHandler, timeout: number = 1000) {
if(this._connectTimeoutHandler) {
clearTimeout(this._connectTimeoutHandler);
this._connectTimeoutHandler = null;
@ -71,6 +71,9 @@ class ServerConnection {
this.updateConnectionState(ConnectionState.CONNECTING);
this._remoteHost = host;
this._remotePort = port;
this._handshakeHandler = handshake;
this._handshakeHandler.setConnection(this);
chat.serverChat().appendMessage("Connecting to " + host + ":" + port);
const self = this;
try {
@ -78,18 +81,19 @@ class ServerConnection {
this.disconnect();
this._client.handleDisconnect(DisconnectReason.CONNECT_FAILURE);
}, timeout);
this._socket = new WebSocket('wss:' + this._remoteHost + ":" + this._remotePort);
let sockCpy;
this._socket = (sockCpy = new WebSocket('wss:' + this._remoteHost + ":" + this._remotePort));
clearTimeout(this._connectTimeoutHandler);
this._connectTimeoutHandler = null;
if(this._socket != sockCpy) return; //Connect timeouted
const _socketCpy = this._socket;
this._socket.onopen = () => {
if(this._socket != _socketCpy) return;
if(this._socket != sockCpy) return;
this.on_connect();
};
this._socket.onclose = event => {
if(this._socket != _socketCpy) return;
if(this._socket != sockCpy) return;
this._client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
code: event.code,
reason: event.reason,
@ -98,13 +102,13 @@ class ServerConnection {
};
this._socket.onerror = e => {
if(this._socket != _socketCpy) return;
if(this._socket != sockCpy) return;
console.log("Got error: (" + self._socket.readyState + ")");
console.log(e);
};
this._socket.onmessage = msg => {
if(this._socket != _socketCpy) return;
if(this._socket != sockCpy) return;
self.handleWebSocketMessage(msg.data);
};
this.updateConnectionState(ConnectionState.INITIALISING);
@ -242,12 +246,65 @@ class ServerConnection {
}
}
class HandshakeHandler {
readonly identity: Identity;
readonly name?: string;
private connection: ServerConnection;
constructor(identity: Identity, name?: string) {
this.identity = identity;
this.name = name;
}
setConnection(con: ServerConnection) {
this.connection = con;
this.connection.commandHandler["handshakeidentityproof"] = this.handleCommandHandshakeIdentityProof.bind(this);
}
startHandshake() {
let data: any = {
intention: 0,
authentication_method: this.identity.type()
};
if(this.identity.type() == IdentitifyType.TEAMSPEAK) {
data.publicKey = (this.identity as TeamSpeakIdentity).publicKey();
} else if(this.identity.type() == IdentitifyType.TEAFORO) {
data.data = (this.identity as TeaForumIdentity).identityDataJson;
}
this.connection.sendCommand("handshakebegin", data).catch(error => {
console.log(error);
//TODO here
});
}
private handleCommandHandshakeIdentityProof(json) {
let proof: string;
if(this.identity.type() == IdentitifyType.TEAMSPEAK) {
proof = (this.identity as TeamSpeakIdentity).signMessage(json[0]["message"]);
} else if(this.identity.type() == IdentitifyType.TEAFORO) {
proof = (this.identity as TeaForumIdentity).identitySign;
}
this.connection.sendCommand("handshakeindentityproof", {proof: proof}).then(() => {
this.connection.sendCommand("clientinit", {
//TODO variables!
client_nickname: this.name ? this.name : this.identity.name(),
client_platform: navigator.platform,
client_version: navigator.userAgent,
client_browser_engine: navigator.product
});
}).catch(error => {
console.error("Got login error");
console.log(error);
}); //TODO handle error
}
}
class ConnectionCommandHandler {
readonly connection: ServerConnection;
constructor(connection) {
this.connection = connection;
this["error"] = this.handleCommandResult;
this["channellist"] = this.handleCommandChannelList;
this["notifychannelcreated"] = this.handleCommandChannelCreate;
@ -278,7 +335,6 @@ class ConnectionCommandHandler {
for(let e of retListeners) {
if(e.code != code) continue;
retListeners.remove(e);
let result = new CommandResult(json);
if(result.success)
e.resolve(result);
@ -306,8 +362,7 @@ class ConnectionCommandHandler {
}
chat.serverChat().name = this.connection._client.channelTree.server.properties["virtualserver_name"];
chat.serverChat().appendMessage("Connected as {0}", true, this.connection._client.getClient().createChatTag(true));
this.connection.on_connected();
globalClient.onConnected();
}
private createChannelFromJson(json, ignoreOrder: boolean = false) {
@ -562,22 +617,26 @@ class ConnectionCommandHandler {
//TODO chat format?
let mode = json["targetmode"];
let invoker = this.connection._client.channelTree.findClient(json["invokerid"]);
if(!invoker) { //TODO ignore?
console.error("Invalid chat message!");
return;
}
if(mode == 1){
let invoker = this.connection._client.channelTree.findClient(json["invokerid"]);
let target = this.connection._client.channelTree.findClient(json["target"]);
if(!invoker) { //TODO spawn chat (Client is may invisible)
console.error("Got private message from invalid client!");
return;
}
if(!target) { //TODO spawn chat (Client is may invisible)
console.error("Got private message from invalid client!");
return;
}
if(invoker == this.connection._client.getClient()) {
let target = this.connection._client.channelTree.findClient(json["target"]);
target.chat(true).appendMessage("<< " + json["msg"]);
} else {
invoker.chat(true).appendMessage(">> " + json["msg"]);
}
} else if(mode == 2) {
chat.channelChat().appendMessage("{0} >> " + json["msg"], true, invoker.createChatTag(true))
chat.channelChat().appendMessage("{0} >> {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"])
} else if(mode == 3) {
chat.serverChat().appendMessage("{0} >> " + json["msg"], true, invoker.createChatTag(true));
chat.serverChat().appendMessage("{0} >> {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"]);
}
}

View file

@ -50,13 +50,11 @@ class MenuEntry {
};
}
}
;
function spawnMenu(x, y, ...entries) {
var menu = $("#contextMenu");
const menu = $("#contextMenu");
menu.empty();
menu.hide();
contextMenuCloseFn = undefined;
console.log(" -> " + ($.isArray(entries) ? "yes" : "no"));
let index = 0;
for (let entry of entries) {
if (entry.type == MenuEntryType.HR) {
@ -75,11 +73,15 @@ function spawnMenu(x, y, ...entries) {
tag.append("<div class='" + icon + "'></div>");
tag.append("<div>" + ($.isFunction(entry.name) ? entry.name() : entry.name) + "</div>");
menu.append(tag);
tag.click(function () {
if ($.isFunction(entry.callback))
entry.callback();
despawnContextMenu();
});
if (entry.disabled)
tag.addClass("disabled");
else {
tag.click(function () {
if ($.isFunction(entry.callback))
entry.callback();
despawnContextMenu();
});
}
}
}
menu.show(100);

View file

@ -1 +1 @@
{"version":3,"file":"contextMenu.js","sourceRoot":"","sources":["contextMenu.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IACrC,yCAAyC;IACzC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QAClD,UAAU;QACV,kBAAkB,EAAE,CAAC;IACzB,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,kBAAkB,GAAG,SAAS,CAAC;AACnC;IACI,IAAI,KAAK,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IAC9B,EAAE,CAAA,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;QAAC,MAAM,CAAC;IACjC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,EAAE,CAAA,CAAC,kBAAkB,CAAC;QAAC,kBAAkB,EAAE,CAAC;AAChD,CAAC;AAED,IAAK,aAKJ;AALD,WAAK,aAAa;IACd,mDAAK,CAAA;IACL,mDAAK,CAAA;IACL,6CAAE,CAAA;IACF,mDAAK,CAAA;AACT,CAAC,EALI,aAAa,KAAb,aAAa,QAKjB;AAED;IACI,MAAM,CAAC,EAAE;QACL,MAAM,CAAC;YACH,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;YAClB,IAAI,EAAE,aAAa,CAAC,EAAE;YACtB,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,EAAE;SACX,CAAC;IACN,CAAC;IAAA,CAAC;IAEF,MAAM,CAAC,KAAK;QACR,MAAM,CAAC;YACH,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;YAClB,IAAI,EAAE,aAAa,CAAC,KAAK;YACzB,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,EAAE;SACX,CAAC;IACN,CAAC;IAAA,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,QAAoB;QAC7B,MAAM,CAAC;YACH,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,aAAa,CAAC,KAAK;YACzB,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,EAAE;SACX,CAAC;IACN,CAAC;CACJ;AAAA,CAAC;AAEF,mBAAmB,CAAC,EAAE,CAAC,EAAE,GAAG,OAKzB;IACC,IAAI,IAAI,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;IACb,IAAI,CAAC,IAAI,EAAE,CAAC;IAEZ,kBAAkB,GAAG,SAAS,CAAC;IAE/B,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,GAAG,CAAA,CAAC,IAAI,KAAK,IAAI,OAAO,CAAC,CAAA,CAAC;QACtB,EAAE,CAAA,CAAC,KAAK,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,IAAI,CAAC,EAAE,CAAA,CAAC,KAAK,CAAC,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1C,kBAAkB,GAAG,KAAK,CAAC,QAAQ,CAAC;QACxC,CAAC;QAAC,IAAI,CAAC,EAAE,CAAA,CAAC,KAAK,CAAC,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1C,IAAI,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YAChE,EAAE,CAAA,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;gBAAC,IAAI,GAAG,YAAY,CAAC;YACzC,IAAI;gBAAC,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;YAE3B,IAAI,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,GAAG,CAAC,MAAM,CAAC,cAAc,GAAG,IAAI,GAAG,UAAU,CAAC,CAAC;YAC/C,GAAG,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;YACxF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAEjB,GAAG,CAAC,KAAK,CAAC;gBACN,EAAE,CAAA,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAClD,kBAAkB,EAAE,CAAC;YACzB,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,oCAAoC;IACpC,IAAI,CAAC,GAAG,CAAC;QACL,KAAK,EAAE,CAAC,GAAG,IAAI;QACf,MAAM,EAAE,CAAC,GAAG,IAAI;KACnB,CAAC,CAAC;AACP,CAAC"}
{"version":3,"file":"contextMenu.js","sourceRoot":"","sources":["contextMenu.ts"],"names":[],"mappings":"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"}

View file

@ -49,21 +49,21 @@ class MenuEntry {
icon: ""
};
}
};
}
function spawnMenu(x, y, ...entries: {
callback: () => void;
type: MenuEntryType;
name: (() => string) | string;
icon: (() => string) | string;
disabled?: boolean;
}[]) {
var menu = $("#contextMenu");
const menu = $("#contextMenu");
menu.empty();
menu.hide();
contextMenuCloseFn = undefined;
console.log(" -> " + ($.isArray(entries) ? "yes" : "no"));
let index = 0;
for(let entry of entries){
@ -79,12 +79,16 @@ function spawnMenu(x, y, ...entries: {
let tag = $.spawn("li");
tag.append("<div class='" + icon + "'></div>");
tag.append("<div>" + ($.isFunction(entry.name) ? entry.name() : entry.name) + "</div>");
menu.append(tag);
tag.click(function () {
if($.isFunction(entry.callback)) entry.callback();
despawnContextMenu();
});
if(entry.disabled) tag.addClass("disabled");
else {
tag.click(function () {
if($.isFunction(entry.callback)) entry.callback();
despawnContextMenu();
});
}
}
}

View file

@ -77,6 +77,7 @@ function loadDebug() {
"js/ui/modal/ModalSettings.js",
"js/ui/modal/ModalCreateChannel.js",
"js/ui/modal/ModalConnect.js",
"js/ui/modal/ModalChangeVolume.js",
"js/ui/channel.js",
"js/ui/client.js",
"js/ui/server.js",
@ -95,6 +96,8 @@ function loadDebug() {
//Load codec
"js/codec/Codec.js",
"js/codec/BasicCodec.js",
"js/codec/CodecWrapper.js",
//Load general stuff
"js/settings.js",
@ -103,7 +106,8 @@ function loadDebug() {
"js/FileManager.js",
"js/client.js",
"js/chat.js",
"js/InfoBar.js"
"js/InfoBar.js",
"js/Identity.js"
])).then(() => {
awaitLoad(loadScripts(["js/main.js"])).then(() => {
console.log("Loaded successfully all scripts!");
@ -120,7 +124,14 @@ function awaitLoad(promises: {path: string, promise: Promise<Boolean>}[]) : Prom
awaiting--;
if(awaiting == 0) resolve();
}).catch(error => {
console.error("Failed to load script " + entry.path);
if(error instanceof TypeError) {
console.error(error);
let name = (error as any).fileName + "@" + (error as any).lineNumber + ":" + (error as any).columnNumber;
displayCriticalError("Failed to execute script <code>" + name + "</code>.<hr>If you believe that it isn't you're mistake<br>then please contact an administrator!", false);
return;
} else {
console.error("Failed to load script " + entry.path);
}
displayCriticalError("Failed to load script " + formatPath(entry.path) + ".<hr>If you believe that it isn't you're mistake<br>then please contact an administrator!", false);
})
}
@ -170,8 +181,8 @@ function loadSide() {
["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
["https://webrtc.github.io/adapter/adapter-latest.js"]
])).then(() => awaitLoad(loadScripts([
["https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"],
["js/TeaWeb-Native.js", "asm/generated/TeaWeb-Native.js"]
["asm/generated/TeaWeb-Native.js", "js/TeaWeb-Native.js"],
["https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"]
]))).then(() => {
//Load the teaweb scripts
loadScript("js/proto.js").then(loadDebug).catch(error => {

View file

@ -1,11 +1,24 @@
/// <reference path="chat.ts" />
/// <reference path="client.ts" />
/// <reference path="Identity.ts" />
/// <reference path="utils/modal.ts" />
/// <reference path="ui/modal/ModalConnect.ts" />
/// <reference path="codec/CodecWrapper.ts" />
/// <reference path="settings.ts" />
let globalClient;
let chat;
let forumIdentity;
function invokeMain() {
AudioController.initializeAudioController();
if (!TSIdentityHelper.setup()) {
console.error("Could not setup the TeamSpeak identity parser!");
return;
}
globalClient = new TSClient();
/** Setup the XF forum identity **/
if (globalClient.settings.static("forum_user_data")) {
forumIdentity = new TeaForumIdentity(globalClient.settings.static("forum_user_data"), globalClient.settings.static("forum_user_sign"));
}
chat = new ChatBox($("#chat"));
globalClient.setup();
//globalClient.startConnection("localhost:19974"); //TODO remove only for testing
@ -16,5 +29,7 @@ function invokeMain() {
event.returnValue = "Are you really sure?<br>You're still connected!";
//event.preventDefault();
});
//console.log("XF: " + globalClient.settings.static("forum_user_data"));
//console.log("XF: " + globalClient.settings.static("forum_user_sign"));
}
//# sourceMappingURL=main.js.map

View file

@ -1 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,kCAAkC;AAClC,uCAAuC;AACvC,iDAAiD;AAEjD,IAAI,YAAY,CAAC;AACjB,IAAI,IAAI,CAAC;AAET;IACI,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC9B,IAAI,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/B,YAAY,CAAC,KAAK,EAAE,CAAC;IACrB,iFAAiF;IAEjF,6BAA6B;IAC7B,8BAA8B;IAE9B,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,UAAU,KAAK;QACnD,EAAE,CAAA,CAAC,YAAY,CAAC,gBAAgB,IAAI,YAAY,CAAC,gBAAgB,CAAC,SAAS,CAAC;YACxE,KAAK,CAAC,WAAW,GAAG,iDAAiD,CAAC;QAC1E,yBAAyB;IAC7B,CAAC,CAAC,CAAC;AACP,CAAC"}
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,kCAAkC;AAClC,oCAAoC;AACpC,uCAAuC;AACvC,iDAAiD;AACjD,8CAA8C;AAC9C,oCAAoC;AAEpC,IAAI,YAAsB,CAAC;AAC3B,IAAI,IAAa,CAAC;AAElB,IAAI,aAA+B,CAAC;AAEpC;IACI,eAAe,CAAC,yBAAyB,EAAE,CAAC;IAC5C,EAAE,CAAA,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAAC,OAAO,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAAC,MAAM,CAAC;IAAC,CAAC;IAE1G,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC9B,mCAAmC;IACnC,EAAE,CAAA,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACjD,aAAa,GAAG,IAAI,gBAAgB,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC3I,CAAC;IAED,IAAI,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/B,YAAY,CAAC,KAAK,EAAE,CAAC;IACrB,iFAAiF;IAEjF,6BAA6B;IAC7B,8BAA8B;IAE9B,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,UAAU,KAAK;QACnD,EAAE,CAAA,CAAC,YAAY,CAAC,gBAAgB,IAAI,YAAY,CAAC,gBAAgB,CAAC,SAAS,CAAC;YACxE,KAAK,CAAC,WAAW,GAAG,iDAAiD,CAAC;QAC1E,yBAAyB;IAC7B,CAAC,CAAC,CAAC;IAGH,wEAAwE;IACxE,wEAAwE;AAC5E,CAAC"}

View file

@ -1,13 +1,26 @@
/// <reference path="chat.ts" />
/// <reference path="client.ts" />
/// <reference path="Identity.ts" />
/// <reference path="utils/modal.ts" />
/// <reference path="ui/modal/ModalConnect.ts" />
/// <reference path="codec/CodecWrapper.ts" />
/// <reference path="settings.ts" />
let globalClient;
let chat;
let globalClient: TSClient;
let chat: ChatBox;
let forumIdentity: TeaForumIdentity;
function invokeMain() {
AudioController.initializeAudioController();
if(!TSIdentityHelper.setup()) { console.error("Could not setup the TeamSpeak identity parser!"); return; }
globalClient = new TSClient();
/** Setup the XF forum identity **/
if(globalClient.settings.static("forum_user_data")) {
forumIdentity = new TeaForumIdentity(globalClient.settings.static("forum_user_data"), globalClient.settings.static("forum_user_sign"));
}
chat = new ChatBox($("#chat"));
globalClient.setup();
//globalClient.startConnection("localhost:19974"); //TODO remove only for testing
@ -20,4 +33,8 @@ function invokeMain() {
event.returnValue = "Are you really sure?<br>You're still connected!";
//event.preventDefault();
});
//console.log("XF: " + globalClient.settings.static("forum_user_data"));
//console.log("XF: " + globalClient.settings.static("forum_user_sign"));
}

View file

@ -22,14 +22,17 @@ if (!Array.prototype.last) {
return this[this.length - 1];
};
}
if (!$.spawn) {
$.spawn = function (tagName) {
return $(document.createElement(tagName));
};
if (typeof ($) !== "undefined") {
if (!$.spawn) {
$.spawn = function (tagName) {
return $(document.createElement(tagName));
};
}
}
if (!String.prototype.format) {
String.prototype.format = function () {
const args = arguments;
let array = args.length == 1 && $.isArray(args[0]);
return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
if (m == "{{") {
return "{";
@ -37,7 +40,7 @@ if (!String.prototype.format) {
if (m == "}}") {
return "}";
}
return args[n];
return array ? args[0][n] : args[n];
});
};
}

View file

@ -1 +1 @@
{"version":3,"file":"proto.js","sourceRoot":"","sources":["proto.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"proto.js","sourceRoot":"","sources":["proto.ts"],"names":[],"mappings":"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"}

View file

@ -13,8 +13,11 @@ interface JQueryStatic<TElement extends Node = HTMLElement> {
spawn<K extends keyof HTMLElementTagNameMap>(tagName: K): JQuery<HTMLElementTagNameMap[K]>;
}
interface String {
format(...fmt): string;
format(arguments: string[]): string;
}
if (!Array.prototype.remove) {
@ -42,19 +45,22 @@ if (!Array.prototype.last){
};
}
if(!$.spawn) {
$.spawn = function<K extends keyof HTMLElementTagNameMap>(tagName: K): JQuery<HTMLElementTagNameMap[K]> {
return $(document.createElement(tagName));
if(typeof ($) !== "undefined") {
if(!$.spawn) {
$.spawn = function<K extends keyof HTMLElementTagNameMap>(tagName: K): JQuery<HTMLElementTagNameMap[K]> {
return $(document.createElement(tagName));
}
}
}
if (!String.prototype.format) {
String.prototype.format = function() {
const args = arguments;
let array = args.length == 1 && $.isArray(args[0]);
return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
if (m == "{{") { return "{"; }
if (m == "}}") { return "}"; }
return args[n];
return array ? args[0][n] : args[n];
});
};
}

View file

@ -25,8 +25,9 @@ class Settings {
let result = this.cacheGlobal[key];
return result ? result : _default;
}
server(key) {
return this.cacheServer[key];
server(key, _default) {
let result = this.cacheServer[key];
return result ? result : _default;
}
changeGlobal(key, value) {
if (this.cacheGlobal[key] == value)
@ -45,7 +46,7 @@ class Settings {
this.save();
}
loadServer() {
if (this.handle.channelTree.server) {
if (!this.handle.channelTree.server) {
this.cacheServer = {};
console.warn("[Settings] tried to load settings for unknown server");
return;
@ -69,7 +70,12 @@ class Settings {
let result = this._staticPropsTag.find("[key='" + key + "']");
if (result.length == 0)
return _default;
return result.attr("value");
return decodeURIComponent(result.attr("value"));
}
deleteStatic(key) {
let result = this._staticPropsTag.find("[key='" + key + "']");
if (result.length != 0)
result.detach();
}
}
Settings.UPDATE_DIRECT = true;

View file

@ -1 +1 @@
{"version":3,"file":"settings.js","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"settings.js","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"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"}

View file

@ -34,8 +34,9 @@ class Settings {
return result ? result : _default;
}
server?(key: string) : string {
return this.cacheServer[key];
server?(key: string, _default?: string) : string {
let result = this.cacheServer[key];
return result ? result : _default;
}
changeGlobal(key: string, value?: string){
@ -59,7 +60,7 @@ class Settings {
}
loadServer() {
if(this.handle.channelTree.server) {
if(!this.handle.channelTree.server) {
this.cacheServer = {};
console.warn("[Settings] tried to load settings for unknown server");
return;
@ -86,6 +87,11 @@ class Settings {
static?(key: string, _default: string = undefined) : string {
let result = this._staticPropsTag.find("[key='" + key + "']");
if(result.length == 0) return _default;
return result.attr("value");
return decodeURIComponent(result.attr("value"));
}
deleteStatic(key: string) {
let result = this._staticPropsTag.find("[key='" + key + "']");
if(result.length != 0) result.detach();
}
}

View file

@ -57,6 +57,7 @@ class ControlBar {
this.handle.settings.changeGlobal("mute_input", this._muteInput ? "1" : "0");
this.updateMicrophoneRecordState();
}
get muteOutput() { return this._muteOutput; }
set muteOutput(flag) {
if (this._muteOutput == flag)
return;

File diff suppressed because one or more lines are too long

View file

@ -75,6 +75,8 @@ class ControlBar {
this.updateMicrophoneRecordState();
}
get muteOutput() : boolean { return this._muteOutput; }
set muteOutput(flag: boolean) {
if(this._muteOutput == flag) return;
this._muteOutput = flag;

View file

@ -116,14 +116,14 @@ class ChannelEntry {
return this.htmlTag.find(".clients").last(); //Here last because from the sibling tag client tags could be before
}
adjustSize(parent = true) {
var size = this.originalHeight;
var subSize = 0;
var clientSize = 0;
var sub = this.siblings(false);
const size = this.originalHeight;
let subSize = 0;
let clientSize = 0;
const sub = this.siblings(false);
sub.forEach(function (e) {
subSize += e.htmlTag.outerHeight(true);
});
var clients = this.clients(false);
const clients = this.clients(false);
clients.forEach(function (e) {
clientSize += e.htmlTag.outerHeight(true);
});
@ -164,6 +164,7 @@ class ChannelEntry {
type: MenuEntryType.ENTRY,
icon: "client-channel_create_sub",
name: "Create sub channel",
disabled: true,
callback: () => {
//TODO here
}
@ -171,6 +172,7 @@ class ChannelEntry {
type: MenuEntryType.ENTRY,
icon: "client-channel_create",
name: "Create channel",
disabled: true,
callback: () => {
//TODO here
}
@ -291,7 +293,7 @@ class ChannelEntry {
tag.attr("oncontextmenu", "chat_channel_contextmenu(this, ...arguments);");
tag.attr("channelId", this.channelId);
tag.attr("channelName", this.channelName());
return tag.wrap("<p/>").parent().html();
return tag.wrap("<p/>").parent();
}
channelType() {
if (this.properties.channel_flag_permanent == "1")

File diff suppressed because one or more lines are too long

View file

@ -143,16 +143,16 @@ class ChannelEntry {
}
adjustSize(parent = true) {
var size = this.originalHeight;
var subSize = 0;
var clientSize = 0;
const size = this.originalHeight;
let subSize = 0;
let clientSize = 0;
var sub = this.siblings(false);
const sub = this.siblings(false);
sub.forEach(function (e) {
subSize += e.htmlTag.outerHeight(true);
});
var clients = this.clients(false);
const clients = this.clients(false);
clients.forEach(function (e) {
clientSize += e.htmlTag.outerHeight(true);
});
@ -197,6 +197,7 @@ class ChannelEntry {
type: MenuEntryType.ENTRY,
icon: "client-channel_create_sub",
name: "Create sub channel",
disabled: true,
callback: () => {
//TODO here
}
@ -204,6 +205,7 @@ class ChannelEntry {
type: MenuEntryType.ENTRY,
icon: "client-channel_create",
name: "Create channel",
disabled: true,
callback: () => {
//TODO here
}
@ -313,7 +315,7 @@ class ChannelEntry {
}
}
createChatTag(braces: boolean = false) : string {
createChatTag(braces: boolean = false) : JQuery {
let tag = $.spawn("div");
tag.css("display", "table");
@ -327,7 +329,7 @@ class ChannelEntry {
tag.attr("oncontextmenu", "chat_channel_contextmenu(this, ...arguments);");
tag.attr("channelId", this.channelId);
tag.attr("channelName", this.channelName());
return tag.wrap("<p/>").parent().html();
return tag.wrap("<p/>").parent();
}
channelType() : ChannelType {

View file

@ -1,4 +1,5 @@
/// <reference path="channel.ts" />
/// <reference path="modal/ModalChangeVolume.ts" />
class ClientEntry {
constructor(clientId, clientName) {
this.properties = {
@ -22,6 +23,7 @@ class ClientEntry {
this.audioController.onSilence = function () {
_this.speaking = false;
};
this.audioController.initialize();
}
currentChannel() { return this._channel; }
clientNickName() { return this.properties.client_nickname; }
@ -85,6 +87,16 @@ class ClientEntry {
}, { width: 400, maxLength: 1024 }).open();
}
}, MenuEntry.HR(), {
type: MenuEntryType.ENTRY,
icon: "client-move_client_to_own_channel",
name: "Move client to your channel",
callback: () => {
this.channelTree.client.serverConnection.sendCommand("clientmove", {
clid: this.clientId(),
cid: this.channelTree.client.getClient().currentChannel().getChannelId()
});
}
}, {
type: MenuEntryType.ENTRY,
icon: "client-kick_channel",
name: "Kick client from channel",
@ -116,6 +128,24 @@ class ClientEntry {
}
}, { width: 400, maxLength: 255 }).open();
}
}, {
type: MenuEntryType.ENTRY,
icon: "client-ban_client",
name: "Ban client",
disabled: true,
callback: () => { }
}, MenuEntry.HR(), {
type: MenuEntryType.ENTRY,
icon: "client-volume",
name: "Change Volume",
callback: () => {
Modals.spawnChangeVolume(this.audioController.volume, volume => {
globalClient.settings.changeServer("volume_client_" + this.clientUid(), volume);
this.audioController.volume = volume;
if (globalClient.selectInfo.currentSelected == this)
globalClient.selectInfo.update();
});
}
}, MenuEntry.CLOSE(on_close));
}
get htmlTag() {
@ -148,7 +178,7 @@ class ClientEntry {
tag.attr("clientId", id);
tag.attr("clientUid", uid);
tag.attr("clientName", name);
return tag.wrap("<p/>").parent().html();
return tag.wrap("<p/>").parent();
}
createChatTag(braces = false) {
return ClientEntry.chatTag(this.clientId(), this.clientNickName(), this.clientUid(), braces);
@ -156,7 +186,6 @@ class ClientEntry {
set speaking(flag) {
if (flag == this._speaking)
return;
console.log("SPeakig " + flag);
this._speaking = flag;
this.updateClientIcon();
}
@ -221,6 +250,10 @@ class ClientEntry {
if (key == "client_away_message" || key == "client_away") {
this.updateAwayMessage();
}
if (key == "client_unique_identifier") {
this.audioController.volume = parseFloat(globalClient.settings.server("volume_client_" + this.clientUid(), "1"));
console.error("Updated volume from config " + this.audioController.volume + " - " + "volume_client_" + this.clientUid() + " - " + globalClient.settings.server("volume_client_" + this.clientUid(), "1"));
}
}
updateVariables() {
if (this.lastVariableUpdate == 0 || new Date().getTime() - 10 * 60 * 1000 > this.lastVariableUpdate) {

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,5 @@
/// <reference path="channel.ts" />
/// <reference path="modal/ModalChangeVolume.ts" />
class ClientEntry {
private _clientId: number;
@ -33,6 +34,7 @@ class ClientEntry {
this.audioController.onSilence = function () {
_this.speaking = false;
};
this.audioController.initialize();
}
currentChannel() { return this._channel; }
@ -104,8 +106,17 @@ class ClientEntry {
}, { width: 400, maxLength: 1024 }).open();
}
},
MenuEntry.HR(),
{
MenuEntry.HR(), {
type: MenuEntryType.ENTRY,
icon: "client-move_client_to_own_channel",
name: "Move client to your channel",
callback: () => {
this.channelTree.client.serverConnection.sendCommand("clientmove", {
clid: this.clientId(),
cid: this.channelTree.client.getClient().currentChannel().getChannelId()
});
}
}, {
type: MenuEntryType.ENTRY,
icon: "client-kick_channel",
name: "Kick client from channel",
@ -139,6 +150,26 @@ class ClientEntry {
}
}, { width: 400, maxLength: 255 }).open();
}
}, {
type: MenuEntryType.ENTRY,
icon: "client-ban_client",
name: "Ban client",
disabled: true,
callback: () => {}
},
MenuEntry.HR(),
{
type: MenuEntryType.ENTRY,
icon: "client-volume",
name: "Change Volume",
callback: () => {
Modals.spawnChangeVolume(this.audioController.volume, volume => {
globalClient.settings.changeServer("volume_client_" + this.clientUid(), volume);
this.audioController.volume = volume;
if(globalClient.selectInfo.currentSelected == this)
globalClient.selectInfo.update();
});
}
},
MenuEntry.CLOSE(on_close)
);
@ -166,7 +197,7 @@ class ClientEntry {
return this._htmlTag = tag;
}
static chatTag(id: number, name: string, uid: string, braces: boolean = false) {
static chatTag(id: number, name: string, uid: string, braces: boolean = false) : JQuery {
let tag = $.spawn("div");
tag.css("cursor", "pointer");
@ -182,16 +213,15 @@ class ClientEntry {
tag.attr("clientId", id);
tag.attr("clientUid", uid);
tag.attr("clientName", name);
return tag.wrap("<p/>").parent().html();
return tag.wrap("<p/>").parent();
}
createChatTag(braces: boolean = false) : string {
createChatTag(braces: boolean = false) : JQuery {
return ClientEntry.chatTag(this.clientId(), this.clientNickName(), this.clientUid(), braces);
}
set speaking(flag) {
if(flag == this._speaking) return;
console.log("SPeakig " + flag);
this._speaking = flag;
this.updateClientIcon();
}
@ -241,7 +271,7 @@ class ClientEntry {
updateVariable(key: string, value: string) {
this.properties[key] = value;
console.debug("Updating client " + this.clientId() + ". Key " + key + " Value: '" + value + "'")
console.debug("Updating client " + this.clientId() + ". Key " + key + " Value: '" + value + "'");
if(key == "client_nickname") {
this.htmlTag.find(".name").text(value);
let chat = this.chat(false);
@ -253,6 +283,10 @@ class ClientEntry {
if(key == "client_away_message" || key == "client_away") {
this.updateAwayMessage();
}
if(key == "client_unique_identifier") {
this.audioController.volume = parseFloat(globalClient.settings.server("volume_client_" + this.clientUid(), "1"));
console.error("Updated volume from config " + this.audioController.volume + " - " + "volume_client_" + this.clientUid() + " - " + globalClient.settings.server("volume_client_" + this.clientUid(), "1"));
}
}
updateVariables(){

View file

@ -0,0 +1,66 @@
/// <reference path="../../utils/modal.ts" />
/// <reference path="../../proto.ts" />
namespace Modals {
export function spawnChangeVolume(current: number, callback: (number) => void) {
let updateCallback: (number) => void;
const connectModal = createModal({
header: function() {
let header = $.spawn("div");
header.text("Change volume");
return header;
},
body: function () {
let tag = $("#tmpl_change_volume").tmpl();
tag.find(".volume_slider").on("change",_ => updateCallback(tag.find(".volume_slider").val()));
tag.find(".volume_slider").on("input",_ => updateCallback(tag.find(".volume_slider").val()));
//connect_address
return tag;
},
footer: function () {
let tag = $.spawn("div");
tag.css("text-align", "right");
tag.css("margin-top", "3px");
tag.css("margin-bottom", "6px");
tag.addClass("modal-button-group");
let buttonReset = $.spawn("button");
buttonReset.text("Reset");
buttonReset.on("click", function () {
updateCallback(100);
});
tag.append(buttonReset);
let buttonCancel = $.spawn("button");
buttonCancel.text("Cancel");
buttonCancel.on("click", function () {
updateCallback(current * 100);
connectModal.close();
});
tag.append(buttonCancel);
let buttonOk = $.spawn("button");
buttonOk.text("OK");
buttonOk.on("click", function () {
connectModal.close();
});
tag.append(buttonOk);
return tag;
},
width: 600
});
updateCallback = value => {
connectModal.htmlTag.find(".volume_slider").val(value);
let display = connectModal.htmlTag.find(".display_volume");
let number = (value - 100);
display.html((number == 0 ? "&plusmn;" : number > 0 ? "+" : "") + number + " %");
callback(value / 100);
};
connectModal.open();
updateCallback(current * 100);
}
}

View file

@ -2,6 +2,7 @@
var Modals;
(function (Modals) {
function spawnConnectModal(defaultHost = "ts.TeaSpeak.de") {
let connectIdentity;
const connectModal = createModal({
header: function () {
let header = $.spawn("div");
@ -11,13 +12,17 @@ var Modals;
body: function () {
let tag = $("#tmpl_connect").contents().clone();
let updateFields = function () {
if (connectIdentity)
tag.find(".connect_nickname").attr("placeholder", connectIdentity.name());
else
tag.find(".connect_nickname").attr("");
let button = tag.parents(".modal-content").find(".connect_connect_button");
let field_address = tag.find(".connect_address");
let address = field_address.val().toString();
let flag_address = !!address.match(Regex.IP_V4) || !!address.match(Regex.DOMAIN);
let field_nickname = tag.find(".connect_nickname");
let nickname = field_nickname.val().toString();
let flag_nickname = nickname.length >= 3 && nickname.length <= 32;
let flag_nickname = nickname.length == 0 || nickname.length >= 3 && nickname.length <= 32;
if (flag_address) {
if (field_address.hasClass("invalid_input"))
field_address.removeClass("invalid_input");
@ -34,7 +39,7 @@ var Modals;
if (!field_nickname.hasClass("invalid_input"))
field_nickname.addClass("invalid_input");
}
if (!flag_nickname || !flag_address) {
if (!flag_nickname || !flag_address || !connectIdentity) {
button.attr("disabled", "true");
}
else {
@ -44,6 +49,54 @@ var Modals;
tag.find(".connect_address").val(defaultHost);
tag.find(".connect_address").on("keyup", () => updateFields());
tag.find(".connect_nickname").on("keyup", () => updateFields());
tag.find(".identity_select").on('change', function () {
globalClient.settings.changeGlobal("connect_identity_type", this.value);
tag.find(".error_message").hide();
tag.find(".identity_config:not(" + ".identity_config_" + this.value + ")").hide();
tag.find(".identity_config_" + this.value).show().trigger('shown');
});
tag.find(".identity_select").val(globalClient.settings.global("connect_identity_type", "forum"));
setTimeout(() => tag.find(".identity_select").trigger('change'), 0); //For some reason could not be run instantly
tag.find(".identity_file").change(function () {
const reader = new FileReader();
reader.onload = function () {
connectIdentity = TSIdentityHelper.loadIdentityFromFileContains(reader.result);
console.log(connectIdentity.uid());
if (!connectIdentity)
tag.find(".error_message").text("Could not read identity! " + TSIdentityHelper.last_error());
else {
tag.find(".identity_string").val(connectIdentity.exported());
globalClient.settings.changeGlobal("connect_identity_teamspeak_identity", connectIdentity.exported());
}
(!!connectIdentity ? tag.hide : tag.show).apply(tag.find(".error_message"));
updateFields();
};
reader.onerror = ev => {
tag.find(".error_message").text("Could not read identity file!").show();
updateFields();
};
reader.readAsText(this.files[0]);
});
tag.find(".identity_string").on('change', function () {
if (this.value.length == 0) {
tag.find(".error_message").text("Please select an identity!");
}
else {
connectIdentity = TSIdentityHelper.loadIdentity(this.value);
if (!connectIdentity)
tag.find(".error_message").text("Could not parse identity! " + TSIdentityHelper.last_error());
else
globalClient.settings.changeGlobal("connect_identity_teamspeak_identity", this.value);
}
(!!connectIdentity ? tag.hide : tag.show).apply(tag.find(".error_message"));
tag.find(".identity_file").val("");
updateFields();
});
tag.find(".identity_string").val(globalClient.settings.global("connect_identity_teamspeak_identity", ""));
tag.find(".identity_config_teamspeak").on('shown', ev => { tag.find(".identity_string").trigger('change'); });
if (!forumIdentity)
tag.find(".identity_config_forum").html("You cant use your TeaSpeak forum account.<br>You're not connected!");
tag.find(".identity_config_forum").on('shown', ev => { connectIdentity = forumIdentity; updateFields(); });
//connect_address
return tag;
},
@ -60,7 +113,7 @@ var Modals;
connectModal.close();
let field_address = tag.parents(".modal-content").find(".connect_address");
let address = field_address.val().toString();
globalClient.startConnection(address);
globalClient.startConnection(address, connectIdentity, tag.parents(".modal-content").find(".connect_nickname").val().toString());
});
tag.append(button);
return tag;
@ -79,4 +132,28 @@ var Modals;
IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
};
})(Modals || (Modals = {}));
/*
<div style="display: flex; justify-content: space-between;">
<div style="text-align: right;">Identity Settings</div>
<select class="identity_select">
<option name="identity_type" value="identity_type_forum">Forum Account</option>
<option name="identity_type" value="identity_type_teamspeak">TeamSpeak</option>
</select>
</div>
<hr>
<div class="identity_config_teamspeak">
Please enter your exported TS3 Identity string bellow or select your exported Identity<br>
<div style="width: 100%; display: flex; flex-direction: row">
<input placeholder="Identity string" style="width: 70%; margin: 5px;" class="identity_string">
<div style="width: 30%; margin: 5px"><input name="identity_file" type="file"></div>
</div>
</div>
<div class="identity_config_forum">
You're using your forum account as verification
</div>
<div style="background-color: red; border-radius: 1px; display: none" class="error_message">
Identity isnt valid!
</div>
*/
//# sourceMappingURL=ModalConnect.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,7 @@
/// <reference path="../../utils/modal.ts" />
namespace Modals {
export function spawnConnectModal(defaultHost: string = "ts.TeaSpeak.de") {
let connectIdentity: Identity;
const connectModal = createModal({
header: function() {
let header = $.spawn("div");
@ -10,8 +11,10 @@ namespace Modals {
body: function () {
let tag = $("#tmpl_connect").contents().clone();
let updateFields = function () {
if(connectIdentity) tag.find(".connect_nickname").attr("placeholder", connectIdentity.name());
else tag.find(".connect_nickname").attr("");
let button = tag.parents(".modal-content").find(".connect_connect_button");
let field_address = tag.find(".connect_address");
@ -20,7 +23,7 @@ namespace Modals {
let field_nickname = tag.find(".connect_nickname");
let nickname = field_nickname.val().toString();
let flag_nickname = nickname.length >= 3 && nickname.length <= 32;
let flag_nickname = nickname.length == 0 || nickname.length >= 3 && nickname.length <= 32;
if(flag_address) {
if(field_address.hasClass("invalid_input"))
@ -38,7 +41,7 @@ namespace Modals {
field_nickname.addClass("invalid_input");
}
if(!flag_nickname || !flag_address) {
if(!flag_nickname || !flag_address || !connectIdentity) {
button.attr("disabled", "true");
} else {
button.removeAttr("disabled");
@ -48,6 +51,57 @@ namespace Modals {
tag.find(".connect_address").val(defaultHost);
tag.find(".connect_address").on("keyup", () => updateFields());
tag.find(".connect_nickname").on("keyup", () => updateFields());
tag.find(".identity_select").on('change', function (this: HTMLSelectElement) {
globalClient.settings.changeGlobal("connect_identity_type", this.value);
tag.find(".error_message").hide();
tag.find(".identity_config:not(" + ".identity_config_" + this.value + ")").hide();
tag.find(".identity_config_" + this.value).show().trigger('shown');
});
tag.find(".identity_select").val(globalClient.settings.global("connect_identity_type", "forum"));
setTimeout(() => tag.find(".identity_select").trigger('change'), 0); //For some reason could not be run instantly
tag.find(".identity_file").change(function (this: HTMLInputElement) {
const reader = new FileReader();
reader.onload = function() {
connectIdentity = TSIdentityHelper.loadIdentityFromFileContains(reader.result);
console.log(connectIdentity.uid());
if(!connectIdentity) tag.find(".error_message").text("Could not read identity! " + TSIdentityHelper.last_error());
else {
tag.find(".identity_string").val((connectIdentity as TeamSpeakIdentity).exported());
globalClient.settings.changeGlobal("connect_identity_teamspeak_identity", (connectIdentity as TeamSpeakIdentity).exported());
}
(!!connectIdentity ? tag.hide : tag.show).apply(tag.find(".error_message"));
updateFields();
};
reader.onerror = ev => {
tag.find(".error_message").text("Could not read identity file!").show();
updateFields();
};
reader.readAsText(this.files[0]);
});
tag.find(".identity_string").on('change', function (this: HTMLInputElement) {
if(this.value.length == 0){
tag.find(".error_message").text("Please select an identity!");
} else {
connectIdentity = TSIdentityHelper.loadIdentity(this.value);
if(!connectIdentity) tag.find(".error_message").text("Could not parse identity! " + TSIdentityHelper.last_error());
else globalClient.settings.changeGlobal("connect_identity_teamspeak_identity", this.value);
}
(!!connectIdentity ? tag.hide : tag.show).apply(tag.find(".error_message"));
tag.find(".identity_file").val("");
updateFields();
});
tag.find(".identity_string").val(globalClient.settings.global("connect_identity_teamspeak_identity", ""));
tag.find(".identity_config_teamspeak").on('shown', ev => { tag.find(".identity_string").trigger('change'); });
if(!forumIdentity)
tag.find(".identity_config_forum").html("You cant use your TeaSpeak forum account.<br>You're not connected!");
tag.find(".identity_config_forum").on('shown', ev => { connectIdentity = forumIdentity; updateFields(); });
//connect_address
return tag;
},
@ -66,7 +120,7 @@ namespace Modals {
let field_address = tag.parents(".modal-content").find(".connect_address");
let address = field_address.val().toString();
globalClient.startConnection(address);
globalClient.startConnection(address, connectIdentity, tag.parents(".modal-content").find(".connect_nickname").val().toString());
});
tag.append(button);
return tag;
@ -86,4 +140,29 @@ namespace Modals {
IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/,
IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
};
}
}
/*
<div style="display: flex; justify-content: space-between;">
<div style="text-align: right;">Identity Settings</div>
<select class="identity_select">
<option name="identity_type" value="identity_type_forum">Forum Account</option>
<option name="identity_type" value="identity_type_teamspeak">TeamSpeak</option>
</select>
</div>
<hr>
<div class="identity_config_teamspeak">
Please enter your exported TS3 Identity string bellow or select your exported Identity<br>
<div style="width: 100%; display: flex; flex-direction: row">
<input placeholder="Identity string" style="width: 70%; margin: 5px;" class="identity_string">
<div style="width: 30%; margin: 5px"><input name="identity_file" type="file"></div>
</div>
</div>
<div class="identity_config_forum">
You're using your forum account as verification
</div>
<div style="background-color: red; border-radius: 1px; display: none" class="error_message">
Identity isnt valid!
</div>
*/

View file

@ -41,10 +41,10 @@ var Modals;
tag.find(".vad_settings .vad_type_settings").hide();
tag.find(".vad_settings .vad_type_" + this.value).show();
globalClient.settings.changeGlobal("vad_type", this.value);
globalClient.voiceConnection.voiceRecorder.reinizaliszeVAD();
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
switch (this.value) {
case "ppt":
let keyCode = Number.parseInt(globalClient.settings.global("vad_ppt_key", 84 /* T */.toString()));
let keyCode = parseInt(globalClient.settings.global("vad_ppt_key", 84 /* T */.toString()));
tag.find(".vat_ppt_key").text(String.fromCharCode(keyCode));
break;
case "vad":
@ -79,7 +79,7 @@ var Modals;
console.log("Got key " + e.keyCode);
modal.close();
globalClient.settings.changeGlobal("vad_ppt_key", e.keyCode.toString());
globalClient.voiceConnection.voiceRecorder.reinizaliszeVAD();
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
tag.find(".vat_ppt_key").text(String.fromCharCode(e.keyCode));
});
modal.open();

File diff suppressed because one or more lines are too long

View file

@ -45,11 +45,11 @@ namespace Modals {
tag.find(".vad_settings .vad_type_settings").hide();
tag.find(".vad_settings .vad_type_" + this.value).show();
globalClient.settings.changeGlobal("vad_type", this.value);
globalClient.voiceConnection.voiceRecorder.reinizaliszeVAD();
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
switch (this.value) {
case "ppt":
let keyCode: number = Number.parseInt(globalClient.settings.global("vad_ppt_key", Key.T.toString()));
let keyCode: number = parseInt(globalClient.settings.global("vad_ppt_key", Key.T.toString()));
tag.find(".vat_ppt_key").text(String.fromCharCode(keyCode));
break;
case "vad":
@ -87,7 +87,7 @@ namespace Modals {
console.log("Got key " + e.keyCode);
modal.close();
globalClient.settings.changeGlobal("vad_ppt_key", e.keyCode.toString());
globalClient.voiceConnection.voiceRecorder.reinizaliszeVAD();
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
tag.find(".vat_ppt_key").text(String.fromCharCode(e.keyCode));
});
modal.open();

View file

@ -26,7 +26,7 @@ class ServerEntry {
tag.addClass("server");
tag.append("<div class=\"icon client-server_green\"></div>");
tag.append("<a class='name'>" + this.properties.virtualserver_name + "</a>");
var serverIcon = $("<span/>");
const serverIcon = $("<span/>");
//we cant spawn an icon on creation :)
serverIcon.append("<div class='icon_property icon_empty'></div>");
tag.append(serverIcon);
@ -52,6 +52,7 @@ class ServerEntry {
}, MenuEntry.CLOSE(on_close));
}
updateProperty(key, value) {
console.trace("Updating property " + key + " => '" + value + "' for the server");
this.properties[key] = value;
if (key == "virtualserver_name") {
this.htmlTag.find(".name").text(value);

View file

@ -1 +1 @@
{"version":3,"file":"server.js","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"server.js","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":"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"}

View file

@ -36,7 +36,7 @@ class ServerEntry {
tag.append("<a class='name'>" + this.properties.virtualserver_name + "</a>");
var serverIcon = $("<span/>");
const serverIcon = $("<span/>");
//we cant spawn an icon on creation :)
serverIcon.append("<div class='icon_property icon_empty'></div>");
tag.append(serverIcon);
@ -69,6 +69,7 @@ class ServerEntry {
}
updateProperty(key, value) : void {
console.trace("Updating property " + key + " => '" + value + "' for the server");
this.properties[key] = value;
if(key == "virtualserver_name") {
this.htmlTag.find(".name").text(value);

View file

@ -3,16 +3,19 @@ var PlayerState;
PlayerState[PlayerState["PREBUFFERING"] = 0] = "PREBUFFERING";
PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING";
PlayerState[PlayerState["BUFFERING"] = 2] = "BUFFERING";
PlayerState[PlayerState["STOPPED"] = 3] = "STOPPED";
PlayerState[PlayerState["STOPPING"] = 3] = "STOPPING";
PlayerState[PlayerState["STOPPED"] = 4] = "STOPPED";
})(PlayerState || (PlayerState = {}));
class AudioController {
constructor() {
this.timeIndex = 0;
this.playerState = PlayerState.STOPPED;
this._playingSources = [];
this.audioCache = [];
this.playingAudioCache = [];
this._volume = 1;
this._codecCache = [];
this._timeIndex = 0;
this.allowBuffering = true;
this.speakerContext = AudioController.globalContext;
this.audioCache = [];
this.onSpeaking = function () { };
this.onSilence = function () { };
}
@ -22,11 +25,21 @@ class AudioController {
this._globalContext = new AudioContext();
return this._globalContext;
}
static initializeAudioController() {
//this._globalReplayScheduler = setInterval(() => { AudioController.invokeNextReplay(); }, 20); //Fix me
}
initialize() {
AudioController._audioInstances.push(this);
}
close() {
AudioController._audioInstances.remove(this);
}
playBuffer(buffer) {
if (buffer.sampleRate != this.speakerContext.sampleRate)
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate!");
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate! (" + buffer.sampleRate + " | " + this.speakerContext.sampleRate + ")");
this.applayVolume(buffer);
this.audioCache.push(buffer);
if (this.playerState == PlayerState.STOPPED) {
if (this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
console.log("[Audio] Starting new playback");
this.playerState = PlayerState.PREBUFFERING;
//New audio
@ -34,7 +47,7 @@ class AudioController {
switch (this.playerState) {
case PlayerState.PREBUFFERING:
case PlayerState.BUFFERING:
if (this.audioCache.length < 5) {
if (this.audioCache.length < 3) {
if (this.playerState == PlayerState.BUFFERING) {
if (this.allowBuffering)
break;
@ -50,55 +63,80 @@ class AudioController {
if (this.allowBuffering)
console.log("[Audio] Buffering succeeded (Replaying now)");
}
this.timeIndex = 0; //Instant replay
this.playerState = PlayerState.PLAYING;
case PlayerState.PLAYING:
this.playCache(this.audioCache);
this.playQueue();
break;
default:
break;
}
}
playCache(cache) {
while (cache.length) {
let buffer = cache.shift();
let source = this.speakerContext.createBufferSource();
source.buffer = buffer;
source.connect(this.speakerContext.destination);
source.onended = () => {
this._playingSources.remove(source);
this.testBufferQueue();
};
if (this.timeIndex == 0) {
this.timeIndex = this.speakerContext.currentTime;
console.log("New next time!");
}
source.start(this.timeIndex);
this.timeIndex += source.buffer.duration;
this._playingSources.push(source);
playQueue() {
let buffer;
while (buffer = this.audioCache.pop_front()) {
if (this._timeIndex < this.speakerContext.currentTime)
this._timeIndex = this.speakerContext.currentTime;
let player = this.speakerContext.createBufferSource();
player.buffer = buffer;
player.onended = () => this.removeNode(player);
this.playingAudioCache.push(player);
player.connect(this.speakerContext.destination);
player.start(this._timeIndex);
this._timeIndex += buffer.duration;
}
}
;
removeNode(node) {
this.playingAudioCache.remove(node);
this.testBufferQueue();
}
stopAudio(now = false) {
this.playerState = PlayerState.STOPPED;
this.playerState = PlayerState.STOPPING;
if (now) {
for (let e of this._playingSources)
e.stop();
this._playingSources = [];
this.playerState = PlayerState.STOPPED;
this.audioCache = [];
for (let entry of this.playingAudioCache)
entry.stop(0);
this.playingAudioCache = [];
}
this.testBufferQueue();
}
testBufferQueue() {
if (this._playingSources.length == 0) {
if (this.playerState != PlayerState.STOPPED) {
if (this.audioCache.length == 0 && this.playingAudioCache.length == 0) {
if (this.playerState != PlayerState.STOPPING) {
this.playerState = PlayerState.BUFFERING;
if (!this.allowBuffering)
console.warn("[Audio] Detected a buffer underflow!");
}
else
else {
this.playerState = PlayerState.STOPPED;
this.onSilence();
}
}
}
close() {
get volume() { return this._volume; }
set volume(val) {
if (this._volume == val)
return;
this._volume = val;
for (let buffer of this.audioCache)
this.applayVolume(buffer);
}
applayVolume(buffer) {
for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
let data = buffer.getChannelData(channel);
for (let sample = 0; sample < data.length; sample++) {
let lane = data[sample];
lane *= this._volume;
data[sample] = lane;
}
}
}
codecCache(codec) {
while (this._codecCache.length <= codec)
this._codecCache.push(new CodecClientCache());
return this._codecCache[codec];
}
}
AudioController._audioInstances = [];
AudioController._timeIndex = 0;
//# sourceMappingURL=AudioController.js.map

View file

@ -1 +1 @@
{"version":3,"file":"AudioController.js","sourceRoot":"","sources":["AudioController.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"AudioController.js","sourceRoot":"","sources":["AudioController.ts"],"names":[],"mappings":"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"}

View file

@ -2,22 +2,84 @@ enum PlayerState {
PREBUFFERING,
PLAYING,
BUFFERING,
STOPPING,
STOPPED
}
class AudioController {
private static _globalContext;
private static _globalContext: AudioContext;
private static _audioInstances: AudioController[] = [];
private static _globalReplayScheduler: NodeJS.Timer;
private static _timeIndex: number = 0;
static get globalContext() : AudioContext {
if(this._globalContext) return this._globalContext;
this._globalContext = new AudioContext();
return this._globalContext;
}
static initializeAudioController() {
//this._globalReplayScheduler = setInterval(() => { AudioController.invokeNextReplay(); }, 20); //Fix me
}
/*
private static joinTracks(tracks: AudioBuffer[]) : Promise<AudioBuffer> {
let length = Math.max.apply(Math, tracks.map(e => e.length));
if(length == 0 || tracks.length == 0) return new Promise<AudioBuffer>((resolve, reject) => {}); //Do nothink
let context = new OfflineAudioContext(2, length, 44100);
//let context = new OfflineAudioContext(tracks[0].numberOfChannels, tracks[0].length, tracks[0].sampleRate);
tracks.forEach(track => {
let player = context.createBufferSource();
player.buffer = track;
player.connect(context.destination);
player.start(0);
});
return context.startRendering();
}
static invokeNextReplay() {
let replay: {controller: AudioController,buffer: AudioBuffer}[] = [];
for(let instance of this._audioInstances)
if(instance.playerState == PlayerState.PLAYING || instance.playerState == PlayerState.STOPPING) {
let entry = {controller: instance, buffer: instance.audioCache.pop_front() };
instance.flagPlaying = !!entry.buffer;
instance.testBufferQueue();
if(!!entry.buffer) replay.push(entry);
} else if(instance.flagPlaying) {
instance.flagPlaying = false;
instance.testBufferQueue();
}
this.joinTracks(replay.map(e => e.buffer)).then(buffer => {
if(this._timeIndex < this._globalContext.currentTime) {
this._timeIndex = this._globalContext.currentTime;
console.log("Resetting time index!");
}
//console.log(buffer.length + "|" + buffer.duration);
//console.log(buffer);
let player = this._globalContext.createBufferSource();
player.buffer = buffer;
player.connect(this._globalContext.destination);
player.start(this._timeIndex);
this._timeIndex += buffer.duration;
});
}
*/
speakerContext: AudioContext;
private timeIndex: number = 0;
private playerState: PlayerState = PlayerState.STOPPED;
private audioCache: AudioBuffer[];
private _playingSources: AudioBufferSourceNode[] = [];
private audioCache: AudioBuffer[] = [];
private playingAudioCache: AudioBufferSourceNode[] = [];
private _volume: number = 1;
private _codecCache: CodecClientCache[] = [];
private _timeIndex: number = 0;
allowBuffering: boolean = true;
//Events
@ -26,18 +88,26 @@ class AudioController {
constructor() {
this.speakerContext = AudioController.globalContext;
this.audioCache = [];
this.onSpeaking = function () { };
this.onSilence = function () { };
}
public initialize() {
AudioController._audioInstances.push(this);
}
public close(){
AudioController._audioInstances.remove(this);
}
playBuffer(buffer: AudioBuffer) {
if (buffer.sampleRate != this.speakerContext.sampleRate)
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate!");
this.audioCache.push(buffer);
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate! (" + buffer.sampleRate + " | " + this.speakerContext.sampleRate + ")");
if(this.playerState == PlayerState.STOPPED) {
this.applayVolume(buffer);
this.audioCache.push(buffer);
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
console.log("[Audio] Starting new playback");
this.playerState = PlayerState.PREBUFFERING;
//New audio
@ -47,7 +117,7 @@ class AudioController {
switch (this.playerState) {
case PlayerState.PREBUFFERING:
case PlayerState.BUFFERING:
if(this.audioCache.length < 5) {
if(this.audioCache.length < 3) {
if(this.playerState == PlayerState.BUFFERING) {
if(this.allowBuffering) break;
} else break;
@ -59,57 +129,86 @@ class AudioController {
if(this.allowBuffering)
console.log("[Audio] Buffering succeeded (Replaying now)");
}
this.timeIndex = 0; //Instant replay
this.playerState = PlayerState.PLAYING;
case PlayerState.PLAYING:
this.playCache(this.audioCache);
this.playQueue();
break;
default:
break;
}
}
private playCache(cache) {
while (cache.length) {
let buffer = cache.shift();
let source = this.speakerContext.createBufferSource();
private playQueue() {
let buffer: AudioBuffer;
while(buffer = this.audioCache.pop_front()) {
if(this._timeIndex < this.speakerContext.currentTime) this._timeIndex = this.speakerContext.currentTime;
source.buffer = buffer;
source.connect(this.speakerContext.destination);
source.onended = () => {
this._playingSources.remove(source);
this.testBufferQueue();
};
if (this.timeIndex == 0) {
this.timeIndex = this.speakerContext.currentTime;
console.log("New next time!");
}
source.start(this.timeIndex);
this.timeIndex += source.buffer.duration;
this._playingSources.push(source);
}
};
let player = this.speakerContext.createBufferSource();
player.buffer = buffer;
player.onended = () => this.removeNode(player);
this.playingAudioCache.push(player);
player.connect(this.speakerContext.destination);
player.start(this._timeIndex);
this._timeIndex += buffer.duration;
}
}
private removeNode(node: AudioBufferSourceNode) {
this.playingAudioCache.remove(node);
this.testBufferQueue();
}
stopAudio(now: boolean = false) {
this.playerState = PlayerState.STOPPED;
this.playerState = PlayerState.STOPPING;
if(now) {
for(let e of this._playingSources)
e.stop();
this._playingSources = [];
this.playerState = PlayerState.STOPPED;
this.audioCache = [];
for(let entry of this.playingAudioCache)
entry.stop(0);
this.playingAudioCache = [];
}
this.testBufferQueue();
}
private testBufferQueue() {
if(this._playingSources.length == 0) {
if(this.playerState != PlayerState.STOPPED) {
if(this.audioCache.length == 0 && this.playingAudioCache.length == 0) {
if(this.playerState != PlayerState.STOPPING) {
this.playerState = PlayerState.BUFFERING;
if(!this.allowBuffering)
console.warn("[Audio] Detected a buffer underflow!");
} else this.onSilence();
} else {
this.playerState = PlayerState.STOPPED;
this.onSilence();
}
}
}
close(){
get volume() : number { return this._volume; }
set volume(val: number) {
if(this._volume == val) return;
this._volume = val;
for(let buffer of this.audioCache)
this.applayVolume(buffer);
}
private applayVolume(buffer: AudioBuffer) {
for(let channel = 0; channel < buffer.numberOfChannels; channel++) {
let data = buffer.getChannelData(channel);
for(let sample = 0; sample < data.length; sample++) {
let lane = data[sample];
lane *= this._volume;
data[sample] = lane;
}
}
}
codecCache(codec: number) : CodecClientCache {
while(this._codecCache.length <= codec)
this._codecCache.push(new CodecClientCache());
return this._codecCache[codec];
}
}

View file

@ -1,12 +1,14 @@
class AudioResampler {
constructor(targetSampleRate = 44100) {
this.targetSampleRate = targetSampleRate;
if (this.targetSampleRate < 3000 || this.targetSampleRate > 384000)
throw "The target sample rate is outside the range [3000, 384000].";
}
resample(buffer) {
if (buffer.sampleRate == this.targetSampleRate)
return new Promise(resolve => resolve(buffer));
let context;
context = new OfflineAudioContext(1, Math.ceil(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
context = new OfflineAudioContext(buffer.numberOfChannels, Math.floor(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
let source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);

View file

@ -1 +1 @@
{"version":3,"file":"AudioResampler.js","sourceRoot":"","sources":["AudioResampler.ts"],"names":[],"mappings":"AAAA;IAGI,YAAY,mBAA2B,KAAK;QACxC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC7C,CAAC;IAED,QAAQ,CAAC,MAAmB;QACxB,EAAE,CAAA,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,gBAAgB,CAAC;YAC1C,MAAM,CAAC,IAAI,OAAO,CAAc,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAEhE,IAAI,OAAO,CAAC;QACZ,OAAO,GAAG,IAAI,mBAAmB,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAElI,IAAI,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC1C,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAEhB,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;IACpC,CAAC;CACJ"}
{"version":3,"file":"AudioResampler.js","sourceRoot":"","sources":["AudioResampler.ts"],"names":[],"mappings":"AAAA;IAGI,YAAY,mBAA2B,KAAK;QACxC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,EAAE,CAAA,CAAC,IAAI,CAAC,gBAAgB,GAAG,IAAI,IAAI,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;YAAC,MAAM,6DAA6D,CAAC;IAC3I,CAAC;IAED,QAAQ,CAAC,MAAmB;QACxB,EAAE,CAAA,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,gBAAgB,CAAC;YAC1C,MAAM,CAAC,IAAI,OAAO,CAAc,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAEhE,IAAI,OAAO,CAAC;QACZ,OAAO,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAEzJ,IAAI,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC1C,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAEhB,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;IACpC,CAAC;CACJ"}

View file

@ -3,6 +3,7 @@ class AudioResampler {
constructor(targetSampleRate: number = 44100){
this.targetSampleRate = targetSampleRate;
if(this.targetSampleRate < 3000 || this.targetSampleRate > 384000) throw "The target sample rate is outside the range [3000, 384000].";
}
resample(buffer: AudioBuffer) : Promise<AudioBuffer> {
@ -10,7 +11,7 @@ class AudioResampler {
return new Promise<AudioBuffer>(resolve => resolve(buffer));
let context;
context = new OfflineAudioContext(1, Math.ceil(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
context = new OfflineAudioContext(buffer.numberOfChannels, Math.floor(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
let source = context.createBufferSource();
source.buffer = buffer;

View file

@ -1,15 +1,61 @@
/// <reference path="../client.ts" />
/// <reference path="../codec/Codec.ts" />
/// <reference path="VoiceRecorder.ts" />
class CodecPoolEntry {
}
class CodecPool {
constructor(handle, index, creator) {
this.entries = [];
this.maxInstances = 2;
this.creator = creator;
this.handle = handle;
this.codecIndex = index;
}
ownCodec(clientId, create = true) {
if (!this.creator)
return null;
let free = 0;
for (let index = 0; index < this.entries.length; index++) {
if (this.entries[index].owner == clientId) {
this.entries[index].last_access = new Date().getTime();
return this.entries[index].instance;
}
else if (free == 0 && this.entries[index].owner == 0) {
free = index;
}
}
if (!create)
return null;
if (free == 0) {
free = this.entries.length;
let entry = new CodecPoolEntry();
entry.instance = this.creator();
entry.instance.initialise();
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
this.entries.push(entry);
}
this.entries[free].owner = clientId;
this.entries[free].last_access = new Date().getTime();
this.entries[free].instance.reset();
return this.entries[free].instance;
}
releaseCodec(clientId) {
for (let index = 0; index < this.entries.length; index++) {
if (this.entries[index].owner == clientId)
this.entries[index].owner = 0;
}
}
}
class VoiceConnection {
constructor(client) {
this.codecs = [
undefined,
undefined,
undefined,
undefined,
undefined,
undefined //opus music
this.codecPool = [
new CodecPool(this, 0, undefined),
new CodecPool(this, 1, undefined),
new CodecPool(this, 2, undefined),
new CodecPool(this, 3, undefined),
new CodecPool(this, 4, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1); }),
new CodecPool(this, 5, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2); }) //opus music
//FIXME Why is it at index 5 currently only 1?
];
this.vpacketId = 0;
this.chunkVPacketId = 0;
@ -17,16 +63,7 @@ class VoiceConnection {
this.voiceRecorder = new VoiceRecorder(this);
this.voiceRecorder.on_data = this.handleVoiceData.bind(this);
this.voiceRecorder.on_end = this.handleVoiceEnded.bind(this);
this.codecs[4] = new OpusCodec(1, OpusType.VOIP);
this.codecs[5] = new OpusCodec(2, OpusType.AUDIO);
for (let index = 0; index < this.codecs.length; index++) {
if (!this.codecs[index])
continue;
let codec = this.codecs[index];
console.log("Got codec " + codec.name());
codec.initialise();
codec.on_encoded_data = buffer => this.sendVoicePacket(buffer, index);
}
this.voiceRecorder.reinitialiseVAD();
}
sendVoicePacket(data, codec) {
if (this.dataChannel) {
@ -35,9 +72,9 @@ class VoiceConnection {
this.vpacketId = 0;
let packet = new Uint8Array(data.byteLength + 2 + 3);
packet[0] = this.chunkVPacketId++ < 5 ? 1 : 0; //Flag header
packet[1] = 0; //Flag frag mented
packet[2] = (this.vpacketId >> 8) & 0xFF; //HIGHT(voiceID)
packet[3] = (this.vpacketId >> 0) & 0xFF; //LOW (voiceID)
packet[1] = 0; //Flag fragmented
packet[2] = (this.vpacketId >> 8) & 0xFF; //HIGHT (voiceID)
packet[3] = (this.vpacketId >> 0) & 0xFF; //LOW (voiceID)
packet[4] = codec; //Codec
packet.set(data, 5);
this.dataChannel.send(packet);
@ -100,6 +137,8 @@ class VoiceConnection {
console.log("Got new data channel!");
}
onDataChannelMessage(message) {
if (this.client.controlBar.muteOutput)
return;
let bin = new Uint8Array(message.data);
let clientId = bin[2] << 8 | bin[3];
let packetId = bin[0] << 8 | bin[1];
@ -110,7 +149,8 @@ class VoiceConnection {
console.error("Having voice from unknown client? (ClientID: " + clientId + ")");
return;
}
if (!this.codecs[codec]) {
let codecPool = this.codecPool[codec];
if (!codecPool) {
console.error("Could not playback codec " + codec);
return;
}
@ -121,9 +161,11 @@ class VoiceConnection {
encodedData = new Uint8Array(message.data, 5);
if (encodedData.length == 0) {
client.getAudioController().stopAudio();
codecPool.releaseCodec(clientId);
}
else {
this.codecs[codec].decodeSamples(encodedData).then(buffer => {
let decoder = codecPool.ownCodec(clientId);
decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData).then(buffer => {
client.getAudioController().playBuffer(buffer);
}).catch(error => {
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
@ -131,14 +173,23 @@ class VoiceConnection {
}
}
handleVoiceData(data, head) {
if (!this.voiceRecorder)
return;
if (head) {
this.chunkVPacketId = 0;
this.client.getClient().speaking = true;
}
this.codecs[4].encodeSamples(data); //TODO Use channel codec!
let encoder = this.codecPool[4].ownCodec(this.client.getClientId());
if (!encoder) {
console.error("Could not reserve encoder!");
return;
}
encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data); //TODO Use channel codec!
//this.client.getClient().getAudioController().play(data);
}
handleVoiceEnded() {
if (!this.voiceRecorder)
return;
console.log("Voice ended");
this.client.getClient().speaking = false;
this.sendVoicePacket(new Uint8Array(0), 4); //TODO Use channel codec!

File diff suppressed because one or more lines are too long

View file

@ -2,6 +2,62 @@
/// <reference path="../codec/Codec.ts" />
/// <reference path="VoiceRecorder.ts" />
class CodecPoolEntry {
instance: BasicCodec;
owner: number;
last_access: number;
}
class CodecPool {
handle: VoiceConnection;
codecIndex: number;
creator: () => BasicCodec;
entries: CodecPoolEntry[] = [];
maxInstances: number = 2;
ownCodec?(clientId: number, create: boolean = true) : BasicCodec {
if(!this.creator) return null;
let free = 0;
for(let index = 0; index < this.entries.length; index++) {
if(this.entries[index].owner == clientId) {
this.entries[index].last_access = new Date().getTime();
return this.entries[index].instance;
} else if(free == 0 && this.entries[index].owner == 0) {
free = index;
}
}
if(!create) return null;
if(free == 0){
free = this.entries.length;
let entry = new CodecPoolEntry();
entry.instance = this.creator();
entry.instance.initialise();
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
this.entries.push(entry);
}
this.entries[free].owner = clientId;
this.entries[free].last_access = new Date().getTime();
this.entries[free].instance.reset();
return this.entries[free].instance;
}
releaseCodec(clientId: number) {
for(let index = 0; index < this.entries.length; index++) {
if(this.entries[index].owner == clientId) this.entries[index].owner = 0;
}
}
constructor(handle: VoiceConnection, index: number, creator: () => BasicCodec){
this.creator = creator;
this.handle = handle;
this.codecIndex = index;
}
}
class VoiceConnection {
client: TSClient;
rtcPeerConnection: RTCPeerConnection;
@ -9,13 +65,14 @@ class VoiceConnection {
voiceRecorder: VoiceRecorder;
private codecs: Codec[] = [
undefined, //Spex
undefined, //Spex
undefined, //Spex
undefined, //CELT Mono
undefined, //opus voice
undefined //opus music
private codecPool: CodecPool[] = [
new CodecPool(this,0,undefined), //Spex
new CodecPool(this,1,undefined), //Spex
new CodecPool(this,2,undefined), //Spex
new CodecPool(this,3,undefined), //CELT Mono
new CodecPool(this,4,() => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1) }), //opus voice
new CodecPool(this,5,() => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2) }) //opus music
//FIXME Why is it at index 5 currently only 1?
];
private vpacketId: number = 0;
@ -26,18 +83,7 @@ class VoiceConnection {
this.voiceRecorder = new VoiceRecorder(this);
this.voiceRecorder.on_data = this.handleVoiceData.bind(this);
this.voiceRecorder.on_end = this.handleVoiceEnded.bind(this);
this.codecs[4] = new OpusCodec(1, OpusType.VOIP);
this.codecs[5] = new OpusCodec(2, OpusType.AUDIO);
for(let index = 0; index < this.codecs.length; index++) {
if(!this.codecs[index]) continue;
let codec = this.codecs[index];
console.log("Got codec " + codec.name());
codec.initialise();
codec.on_encoded_data = buffer => this.sendVoicePacket(buffer, index);
}
this.voiceRecorder.reinitialiseVAD();
}
sendVoicePacket(data: Uint8Array, codec: number) {
@ -46,9 +92,9 @@ class VoiceConnection {
if(this.vpacketId > 65535) this.vpacketId = 0;
let packet = new Uint8Array(data.byteLength + 2 + 3);
packet[0] = this.chunkVPacketId++ < 5 ? 1 : 0; //Flag header
packet[1] = 0; //Flag frag mented
packet[2] = (this.vpacketId >> 8) & 0xFF; //HIGHT(voiceID)
packet[3] = (this.vpacketId >> 0) & 0xFF; //LOW (voiceID)
packet[1] = 0; //Flag fragmented
packet[2] = (this.vpacketId >> 8) & 0xFF; //HIGHT (voiceID)
packet[3] = (this.vpacketId >> 0) & 0xFF; //LOW (voiceID)
packet[4] = codec; //Codec
packet.set(data, 5);
this.dataChannel.send(packet);
@ -119,6 +165,8 @@ class VoiceConnection {
}
onDataChannelMessage(message) {
if(this.client.controlBar.muteOutput) return;
let bin = new Uint8Array(message.data);
let clientId = bin[2] << 8 | bin[3];
let packetId = bin[0] << 8 | bin[1];
@ -129,10 +177,13 @@ class VoiceConnection {
console.error("Having voice from unknown client? (ClientID: " + clientId + ")");
return;
}
if(!this.codecs[codec]) {
let codecPool = this.codecPool[codec];
if(!codecPool) {
console.error("Could not playback codec " + codec);
return;
}
let encodedData;
if(message.data.subarray)
encodedData = message.data.subarray(5);
@ -140,8 +191,10 @@ class VoiceConnection {
if(encodedData.length == 0) {
client.getAudioController().stopAudio();
codecPool.releaseCodec(clientId);
} else {
this.codecs[codec].decodeSamples(encodedData).then(buffer => {
let decoder = codecPool.ownCodec(clientId);
decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData).then(buffer => {
client.getAudioController().playBuffer(buffer);
}).catch(error => {
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
@ -150,15 +203,24 @@ class VoiceConnection {
}
private handleVoiceData(data: AudioBuffer, head: boolean) {
if(!this.voiceRecorder) return;
if(head) {
this.chunkVPacketId = 0;
this.client.getClient().speaking = true;
}
this.codecs[4].encodeSamples(data); //TODO Use channel codec!
let encoder = this.codecPool[4].ownCodec(this.client.getClientId());
if(!encoder) {
console.error("Could not reserve encoder!");
return;
}
encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4),data); //TODO Use channel codec!
//this.client.getClient().getAudioController().play(data);
}
private handleVoiceEnded() {
if(!this.voiceRecorder) return;
console.log("Voice ended");
this.client.getClient().speaking = false;
this.sendVoicePacket(new Uint8Array(0), 4); //TODO Use channel codec!

View file

@ -19,9 +19,9 @@ class VoiceRecorder {
this.microphoneStream = undefined;
this.mediaStream = undefined;
this._chunkCount = 0;
this._deviceId = "default";
this.handle = handle;
this.userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
this._deviceId = handle.client.settings.global("microphone_id", "default");
this.audioContext = AudioController.globalContext;
this.processor = this.audioContext.createScriptProcessor(VoiceRecorder.BUFFER_SIZE, VoiceRecorder.CHANNELS, VoiceRecorder.CHANNELS);
this.processor.addEventListener('audioprocess', ev => {
@ -56,10 +56,10 @@ class VoiceRecorder {
getMicrophoneStream() {
return this.microphoneStream;
}
reinizaliszeVAD() {
let type = this.handle.client.settings.global("vad_type", "ppt");
reinitialiseVAD() {
let type = this.handle.client.settings.global("vad_type", "vad");
if (type == "ppt") {
let keyCode = parseInt(globalClient.settings.global("vad_ppt_key", 84 /* T */.toString()));
let keyCode = parseInt(this.handle.client.settings.global("vad_ppt_key", 84 /* T */.toString()));
if (!(this.getVADHandler() instanceof PushToTalkVAD))
this.setVADHander(new PushToTalkVAD(keyCode));
else
@ -72,7 +72,7 @@ class VoiceRecorder {
else if (type == "vad") {
if (!(this.getVADHandler() instanceof VoiceActivityDetectorVAD))
this.setVADHander(new VoiceActivityDetectorVAD());
let threshold = parseInt(globalClient.settings.global("vad_threshold", "50"));
let threshold = parseInt(this.handle.client.settings.global("vad_threshold", "50"));
this.getVADHandler().percentageThreshold = threshold;
}
else {
@ -104,6 +104,7 @@ class VoiceRecorder {
if (this._deviceId == device)
return;
this._deviceId = device;
this.handle.client.settings.changeGlobal("microphone_id", device);
if (this._recording) {
this.stop();
this.start(device);

File diff suppressed because one or more lines are too long

View file

@ -39,12 +39,14 @@ class VoiceRecorder {
private vadHandler: VoiceActivityDetector;
private _chunkCount: number = 0;
private _deviceId: string = "default";
private _deviceId: string;
constructor(handle: VoiceConnection) {
this.handle = handle;
this.userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
this._deviceId = handle.client.settings.global("microphone_id", "default");
this.audioContext = AudioController.globalContext;
this.processor = this.audioContext.createScriptProcessor(VoiceRecorder.BUFFER_SIZE, VoiceRecorder.CHANNELS, VoiceRecorder.CHANNELS);
@ -88,10 +90,10 @@ class VoiceRecorder {
return this.microphoneStream;
}
reinizaliszeVAD() {
let type = this.handle.client.settings.global("vad_type", "ppt");
reinitialiseVAD() {
let type = this.handle.client.settings.global("vad_type", "vad");
if(type == "ppt") {
let keyCode: number = parseInt(globalClient.settings.global("vad_ppt_key", Key.T.toString()));
let keyCode: number = parseInt(this.handle.client.settings.global("vad_ppt_key", Key.T.toString()));
if(!(this.getVADHandler() instanceof PushToTalkVAD))
this.setVADHander(new PushToTalkVAD(keyCode));
else (this.getVADHandler() as PushToTalkVAD).key = keyCode;
@ -101,7 +103,7 @@ class VoiceRecorder {
} else if(type == "vad") {
if(!(this.getVADHandler() instanceof VoiceActivityDetectorVAD))
this.setVADHander(new VoiceActivityDetectorVAD());
let threshold = parseInt(globalClient.settings.global("vad_threshold", "50"));
let threshold = parseInt(this.handle.client.settings.global("vad_threshold", "50"));
(this.getVADHandler() as VoiceActivityDetectorVAD).percentageThreshold = threshold;
} else {
console.warn("Invalid VAD handler! (" + type + ")");
@ -132,6 +134,7 @@ class VoiceRecorder {
changeDevice(device: string) {
if(this._deviceId == device) return;
this._deviceId = device;
this.handle.client.settings.changeGlobal("microphone_id", device);
if(this._recording) {
this.stop();
this.start(device);

View file

@ -3,6 +3,14 @@
<head>
<meta charset="UTF-8">
<title>TeaSpeak-Web client templates</title>
<!-- Imports just for my IDE -->
<link rel="stylesheet" href="css/ts/tab.css" type="text/css">
<link rel="stylesheet" href="css/ts/chat.css" type="text/css">
<link rel="stylesheet" href="css/ts/client.css" type="text/css">
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
<link rel="stylesheet" href="css/general.css" type="text/css">
</head>
<body>
<!-- Template for chennel create & edit-->
@ -110,7 +118,32 @@
</div>
<div>
<div>Nickname:</div>
<input type="text" style="width: 100%" class="connect_nickname" value="A TeaSpeak web user">
<input type="text" style="width: 100%" class="connect_nickname" value="">
</div>
<hr>
<div class="group_box">
<div style="display: flex; justify-content: space-between;">
<div style="text-align: right;">Identity Settings</div>
<select class="identity_select">
<option name="identity_type" value="forum">Forum Account</option>
<option name="identity_type" value="teamspeak">TeamSpeak</option>
</select>
</div>
<hr>
<div class="identity_config_teamspeak identity_config">
Please enter your exported TS3 Identity string bellow or select your exported Identity<br>
<div style="width: 100%; display: flex; flex-direction: row">
<input placeholder="Identity string" style="width: 70%; margin: 5px;" class="identity_string">
<div style="width: 30%; margin: 5px"><input class="identity_file" type="file"></div>
</div>
</div>
<div class="identity_config_forum identity_config">
You're using your forum account as verification
</div>
<div style="background-color: red; border-radius: 1px; display: none" class="error_message">
Identity isnt valid!
</div>
</div>
</div>
</template>
@ -170,5 +203,12 @@
</x-entry>
</x-tab>
</template>
<template id="tmpl_change_volume">
<div style="display: flex; justify-content: center; vertical-align: center">
<input type="range" min="0" max="200" value="100" class="volume_slider" style="width: 100%">
<div class="display_volume" style="width: 60px; align-self: center; text-align: center">&plusmn;0 %</div>
</div>
</template>
</body>
</html>

View file

@ -1,26 +0,0 @@
<!--
HTML index for the example globalClient.
Brian Ho
brian@brkho.com
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example Client</title>
<script src="../vendor/jquery/jquery.min.js"></script>
<script src="../vendor/aurora/aurora.js"></script>
<script src="../vendor/aurora/ogg.js"></script>
<script src="../vendor/aurora/opus.js"></script>
<script src="../vendor/opus/opus_to_pcm.js"></script>
</head>
<body>
<button onclick="connect()">Connect</button>
<button onclick="ping()">Ping</button>
<audio controls="true" id="audio" src=""></audio>
<script src="webrtc.js"></script>
</body>
</html>

View file

@ -1,172 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Ogg Opus Encoder Example</title>
<script src="../vendor/opus-recorder/recorder.js"></script>
<style type='text/css'>
ul { list-style: none; }
li { margin: 1em; }
audio { display: block; }
</style>
</head>
<body>
<h1>Encoder example</h1>
<p>Before you enable monitoring, make sure to either plug in headphones or turn the volume down.</p>
<p>This ogg opus implementation does not support more than 2 channels.</p>
<h2>Options</h2>
<div>
<label>monitorGain</label>
<input id="monitorGain" type="number" value="0" />
</div>
<div>
<label>numberOfChannels</label>
<input id="numberOfChannels" type="number" value="1" />
</div>
<div>
<label>encoderSampleRate</label>
<input id="encoderSampleRate" type="number" value="48000" />
</div>
<div>
<label>bitRate</label>
<input id="bitRate" type="number" value="64000" />
</div>
<h2>Commands</h2>
<button id="start">start</button>
<button id="pause" disabled>pause</button>
<button id="resume" disabled>resume</button>
<button id="stopButton" disabled>stop</button>
<h2>Recordings</h2>
<ul id="recordingslist"></ul>
<h2>Log</h2>
<pre id="log"></pre>
<script>
function screenLogger(text, data) {
log.innerHTML += "\n" + text + " " + (data || '');
}
if (!Recorder.isRecordingSupported()) {
screenLogger("Recording features are not supported in your browser.");
}
else {
var recorder = new Recorder({
monitorGain: parseInt(monitorGain.value, 10),
numberOfChannels: parseInt(numberOfChannels.value, 10),
encoderBitRate: parseInt(bitRate.value,10),
encoderSampleRate: parseInt(encoderSampleRate.value,10),
encoderPath: "../vendor/opus-recorder/encoderWorker.js",
bufferLength: 0,
encoderFrameSize: 10,
streamPages: true,
maxBuffersPerPage: 1
});
pause.addEventListener( "click", function(){ recorder.pause(); });
resume.addEventListener( "click", function(){ recorder.resume(); });
stopButton.addEventListener( "click", function(){ recorder.stop(); });
start.addEventListener( "click", function(){
recorder.start().catch(function(e){
screenLogger('Error encountered: ' + e.message );
});
});
recorder.onstart = function(e){
screenLogger('Recorder is started');
start.disabled = resume.disabled = true;
pause.disabled = stopButton.disabled = false;
};
recorder.onstop = function(e){
screenLogger('Recorder is stopped');
start.disabled = false;
pause.disabled = resume.disabled = stopButton.disabled = true;
};
recorder.onpause = function(e){
screenLogger('Recorder is paused');
pause.disabled = start.disabled = true;
resume.disabled = stopButton.disabled = false;
};
recorder.onresume = function(e){
screenLogger('Recorder is resuming');
start.disabled = resume.disabled = true;
pause.disabled = stopButton.disabled = false;
};
//X
var decoderWorker = new Worker('../vendor/opus-recorder/decoderWorker.js');
decoderWorker.postMessage({
command:'init',
decoderSampleRate: parseInt(encoderSampleRate.value,10),
outputBufferSampleRate: parseInt(encoderSampleRate.value,10)
});
const audioContext = new AudioContext();
const speakers = audioContext.destination;
const audioBuffer = audioContext.createBuffer(1, parseInt(encoderSampleRate.value,10), parseInt(encoderSampleRate.value,10));
decoderWorker.onmessage = function(e){
// null means decoder is finished
if (e.data === null) {
console.log("DONE!");
}
// e.data contains decoded buffers as float32 values
else {
console.log("Data: ");
console.log(e);
audioBuffer.getChannelData(0).set(e.data[0]);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(speakers);
source.start();
}
};
recorder.ondataavailable = function( typedArray ){
console.log("Page");
//console.log(typedArray);
decoderWorker.postMessage({
command: 'decode',
pages: typedArray
}, [typedArray.buffer] );
/*
var dataBlob = new Blob( [typedArray], { type: 'audio/ogg' } );
var fileName = new Date().toISOString() + ".opus";
var url = URL.createObjectURL( dataBlob );
var audio = document.createElement('audio');
audio.controls = true;
audio.src = url;
var link = document.createElement('a');
link.href = url;
link.download = fileName;
link.innerHTML = link.download;
var li = document.createElement('li');
li.appendChild(link);
li.appendChild(audio);
recordingslist.appendChild(li);
*/
};
}
</script>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,172 +0,0 @@
/**
* This a minimal fully functional example for setting up a client written in JavaScript that
* communicates with a server via WebRTC data channels. This uses WebSockets to perform the WebRTC
* handshake (offer/accept SDP) with the server. We only use WebSockets for the initial handshake
* because TCP often presents too much latency in the context of real-time action games. WebRTC
* data channels, on the other hand, allow for unreliable and unordered message sending via SCTP
*
* Brian Ho
* brian@brkho.com
*/
/*
const audioContext = new AudioContext();
const speakers = audioContext.destination;
const audioBuffer = audioContext.createBuffer(1, 48000, 48000);
var mediaSource = new MediaSource();
const audio = document.getElementById("audio");
audio.src = window.URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', function() {
var sourceBuffer = mediaSource.addSourceBuffer('audio/webm;codecs=opus');
sourceBuffer.appendWindowStart = 0;
sourceBuffer.mode = 'segments';
navigator.getUserMedia({video: false, audio: true}, function (stream) {
var options = {
audioBitsPerSecond: 48000, //128000,
//videoBitsPerSecond : 2500000,
mimeType: "audio/webm;codecs=opus" //
};
var mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = function (e) {
let now = new Date().getUTCMilliseconds();
var fileReader = new FileReader();
fileReader.onload = function () {//onloadend
console.log(e);
console.log(fileReader.result);
/*
audioBuffer.getChannelData(0).set(fileReader.result);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(speakers);
source.start();
// single opus packet and it is a typedArray
// decoder.decode(fileReader.result);
let buf = new Uint8Array(fileReader.result);
buf.timestampOffset = 0;
sourceBuffer.appendBuffer(buf);
if(audio.paused) {
audio.play(0);
}
let later = new Date().getUTCMilliseconds();
console.log(later - now);
console.log(sourceBuffer.buffered);
if (i === NUM_CHUNKS - 1) {
mediaSource.endOfStream();
} else {
if ($("audio").paused) {
// start playing after first chunk is appended
$("audio").play();
}
//readChunk(++i);
console.log("Read chunk?");
}
};
fileReader.readAsArrayBuffer(e.data);
};
mediaRecorder.start(20);
}, function () {
console.log("Cant applay audio");
});
});
// Callback for when the data channel was successfully opened.
var decoder = new Decoder.OpusToPCM({
channels: 1,
fallback: true
});
decoder.decoder.on("error", function (pcmData) {
//do whatever you want to do with PCM data
console.log("PCM:");
console.log(pcmData);
});
decoder.on("decode", function (pcmData) {
//do whatever you want to do with PCM data
console.log("DDDD:");
console.log(pcmData);
audioBuffer.getChannelData(0).set(pcmData);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(speakers);
source.start();
});
console.log(decoder);
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// TODO: Figure out what else we need and give the user feedback if he doesn't
// support microphone input.
if (navigator.getUserMedia) {
captureMicrophone();
}
// First Step - Capture microphone and process the input
function captureMicrophone() {
// process input from microphone
const processAudio = ev => processBuffer(ev.inputBuffer.getChannelData(CHANNEL));
// setup media stream from microphone
const microphoneStream = stream => {
const microphone = audioContext.createMediaStreamSource(stream);
microphone.connect(processor);
// #1 If we don't pass through to speakers 'audioprocess' won't be triggerd
processor.connect(mute);
};
// TODO: Handle error properly (see todo above - but probably more specific)
const userMediaError = err => console.error(err);
// Second step - Process buffer and output to speakers
const processBuffer = buffer => {
console.log(buffer);
audioBuffer.getChannelData(CHANNEL).set(buffer);
// We could move this out but that would affect audio quality
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(speakers);
source.start();
}
const audioContext = new AudioContext();
const speakers = audioContext.destination;
// We currently only operate on this channel we might need to add a couple
// lines of code if this fact changes
const CHANNEL = 0;
const CHANNELS = 1;
const BUFFER_SIZE = 4096;
const audioBuffer = audioContext.createBuffer(CHANNELS, BUFFER_SIZE, audioContext.sampleRate);
const processor = audioContext.createScriptProcessor(BUFFER_SIZE, CHANNELS, CHANNELS);
// #2 Not needed we could directly pass through to speakers since there's no
// data anyway but just to be sure that we don't output anything
const mute = audioContext.createGain();
//mute.gain.value = 1;
mute.connect(speakers);
processor.addEventListener('audioprocess', processAudio);
navigator.getUserMedia({audio: true}, microphoneStream, userMediaError);
}
*/
//###########################################################################################
console.log(AV.Decoder.find("opus")());
var player = AV.Player.fromURL('videoplayback.opus');
player.play();

View file

@ -1,4 +1,4 @@
{
//"extends": "./tsconfig/tsconfig_debug.json"
"extends": "./tsconfig/tsconfig_release.json"
"extends": "./tsconfig/tsconfig_debug.json"
//"extends": "./tsconfig/tsconfig_release.json"
}

View file

@ -5,6 +5,7 @@
"sourceMap": true
},
"exclude": [
"../node_modules"
"../node_modules",
"../js/codec/workers"
]
}

View file

@ -4,6 +4,11 @@
"outFile": "../generated/js/client.js"
},
"exclude": [
"../js/load.ts"
"../js/load.ts",
"../node_modules",
"../js/codec/workers"
],
"include": [
"../js/**/*"
]
}