Merging material with master

This commit is contained in:
WolverinDEV 2019-02-17 21:55:19 +01:00
commit 65e6c5b1f7
8 changed files with 287 additions and 10 deletions

6
.gitmodules vendored
View file

@ -2,12 +2,6 @@
path = asm/libraries/opus
url = https://github.com/xiph/opus.git
branch = 1.1.2
[submodule "asm/libraries/tomcrypt"]
path = asm/libraries/tomcrypt
url = http://git.mcgalaxy.de:8090/WolverinDEV/tomcrypt.git
[submodule "asm/libraries/tommath"]
path = asm/libraries/tommath
url = http://git.mcgalaxy.de:8090/WolverinDEV/tommath.git
[submodule "vendor/bbcode"]
path = vendor/bbcode
url = https://github.com/WolverinDEV/Extendible-BBCode-Parser.git

@ -1 +1 @@
Subproject commit b97dd67fdc75a39d0fc99ceee573921ba3e73b1f
Subproject commit 655cc54c564b84ef2827f0b2152ce3811046201e

@ -1 +0,0 @@
Subproject commit 420b947c41df88c6fb32a29add3b12e8ed69b323

@ -1 +0,0 @@
Subproject commit fe6cc64884b2b408b607389f46266d44fd942a79

11
asm/make_opus.sh Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
base_dir=`pwd`
cd "$(dirname $0)/libraries/opus/"
git checkout v1.1.2
./autogen.sh
emconfigure ./configure --disable-extra-programs --disable-doc --disable-rtcd
emmake make
cd ${base_dir}

View file

@ -0,0 +1,42 @@
# File structure
The TeaSpeak web client is separated into 2 different parts.
## I) Application files
Application files are all files which directly belong to the app itself.
Like the javascript files who handle the UI stuff or even translation templates.
Theses files are separated into two type of files.
1. [Shared application files](#1-shared-application-files)
2. [Web application files](#2-web-application-files)
### 1. Shared application files
Containing all files used by the TeaSpeak client and the Web client.
All of these files will be found within the folder `shared`.
This folder follows the general application file structure.
More information could be found [here](#application-file-structure)
### 2. Web application files
All files which only belong to a browser only instance.
All of these files will be found within the folder `web`.
This folder follows the general application file structure.
More information could be found [here](#application-file-structure)
### application file structure
Every application root contains several subfolders.
In the following list will be listed which files belong to which folder
| Folder | Description |
| --- | --- |
| `audio` | This folder contains all audio files used by the application. More information could be found [here](). |
| `css` | This folder contains all style sheets used by the application. More information could be found [here](). |
| `js` | This folder contains all javascript files used by the application. More information could be found [here](). |
| `html` | This folder contains all HTML and PHP files used by the application. More information could be found [here](). |
| `i18n` | This folder contains all default translations. Information about the translation system could be found [here](). |
| `img` | This folder contains all image files. |
## I) Additional tools
## Environment builder
The environment builder is one of the most important tools of the entire project.
This tool, basically implemented in the file `files.php`, will be your helper while live developing.
What this tool does is, it creates a final environment where you could navigate to with your browser.
It merges all the type separated files, which had been listed above ([here](#application-file-structure)).

View file

@ -373,7 +373,7 @@ class IconManager {
return $.spawn("div").addClass("icon client-group_" + id);
let tag = $.spawn("div");
tag.addClass("icon_empty");
tag.addClass("icon-container icon_empty");
let img = $.spawn("img");
img.attr("width", 16).attr("height", 16).attr("alt", "");

232
shared/js/stats.ts Normal file
View file

@ -0,0 +1,232 @@
namespace stats {
const LOG_PREFIX = "[Statistics] ";
export enum CloseCodes {
UNSET = 3000,
RECONNECT = 3001,
INTERNAL_ERROR = 3002,
BANNED = 3100,
}
enum ConnectionState {
CONNECTING,
INITIALIZING,
CONNECTED,
UNSET
}
export class SessionConfig {
/*
* All collected statistics will only be cached by the stats server.
* No data will be saved.
*/
volatile_collection_only?: boolean;
/*
* Anonymize all IP addresses which will be provided while the stats collection.
* This option is quite useless when volatile_collection_only is active.
*/
anonymize_ip_addresses?: boolean;
}
export class Config extends SessionConfig {
verbose?: boolean;
reconnect_interval?: number;
}
export interface UserCountData {
online_users: number;
unique_online_users: number;
}
export type UserCountListener = (data: UserCountData) => any;
let reconnect_timer: NodeJS.Timer;
let current_config: Config;
let last_user_count_update: number;
let user_count_listener: UserCountListener[] = [];
const DEFAULT_CONFIG: Config = {
verbose: true,
reconnect_interval: 5000,
anonymize_ip_addresses: true,
volatile_collection_only: false
};
function initialize_config_object(target_object: any, source_object: any) : any {
for(const key of Object.keys(source_object)) {
if(typeof(source_object[key]) === 'object')
initialize_config_object(target_object[key] || (target_object[key] = {}), source_object[key]);
if(typeof(target_object[key]) !== 'undefined')
continue;
target_object[key] = source_object[key];
}
return target_object;
}
export function initialize(config: Config) {
current_config = initialize_config_object(config || {}, DEFAULT_CONFIG);
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Initializing statistics with this config: %o"), current_config);
connection.start_connection();
}
export function register_user_count_listener(listener: UserCountListener) {
user_count_listener.push(listener);
}
export function all_user_count_listener() : UserCountListener[] {
return user_count_listener;
}
export function deregister_user_count_listener(listener: UserCountListener) {
user_count_listener.remove(listener);
}
namespace connection {
let connection: WebSocket;
export let connection_state: ConnectionState = ConnectionState.UNSET;
export function start_connection() {
cancel_reconnect();
if(connection) {
const connection_copy = connection;
connection = undefined;
connection_copy.close(3001);
}
connection_state = ConnectionState.CONNECTING;
connection = new WebSocket('wss://web-stats.teaspeak.de:1774');
{
const connection_copy = connection;
connection.onclose = (event: CloseEvent) => {
if(connection_copy !== connection) return;
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Lost connection to statistics server (Connection closed). Reason: %o. Event object: %o"), CloseCodes[event.code] || event.code, event);
if(event.code != CloseCodes.BANNED)
invoke_reconnect();
};
connection.onopen = () => {
if(connection_copy !== connection) return;
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Successfully connected to server. Initializing session."));
connection_state = ConnectionState.INITIALIZING;
initialize_session();
};
connection.onerror = (event: ErrorEvent) => {
if(connection_copy !== connection) return;
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Received an error. Closing connection. Object: %o"), event);
connection.close(CloseCodes.INTERNAL_ERROR);
this.invoke_reconnect();
};
connection.onmessage = (event: MessageEvent) => {
if(connection_copy !== connection) return;
if(typeof(event.data) !== 'string') {
if(current_config.verbose)
console.warn(LOG_PREFIX + tr("Received an message which isn't a string. Event object: %o"), event);
return;
}
handle_message(event.data as string);
};
}
}
function invoke_reconnect() {
if(reconnect_timer) {
clearTimeout(reconnect_timer);
reconnect_timer = undefined;
}
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Scheduled reconnect in %dms"), current_config.reconnect_interval);
reconnect_timer = setTimeout(() => {
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Reconnecting"));
start_connection();
}, current_config.reconnect_interval);
}
export function cancel_reconnect() {
if(reconnect_timer) {
clearTimeout(reconnect_timer);
reconnect_timer = undefined;
}
}
function send_message(type: string, data: any) {
connection.send(JSON.stringify({
type: type,
data: data
}));
}
function initialize_session() {
const config_object = {};
for(const key in SessionConfig) {
if(SessionConfig.hasOwnProperty(key))
config_object[key] = current_config[key];
}
send_message('initialize', {
config: config_object
})
}
function handle_message(message: string) {
const data_object = JSON.parse(message);
const type = data_object.type as string;
const data = data_object.data;
if(typeof(handler[type]) === 'function') {
if(current_config.verbose)
console.debug(LOG_PREFIX + tr("Handling message of type %s"), type);
handler[type](data);
} else if(current_config.verbose) {
console.warn(LOG_PREFIX + tr("Received message with an unknown type (%s). Dropping message. Full message: %o"), type, data_object);
}
}
namespace handler {
interface NotifyUserCount extends UserCountData { }
function handle_notify_user_count(data: NotifyUserCount) {
last_user_count_update = Date.now();
for(const listener of [...user_count_listener])
listener(data);
}
interface NotifyInitialized {}
function handle_notify_initialized(json: NotifyInitialized) {
if(current_config.verbose)
console.log(LOG_PREFIX + tr("Session successfully initialized."));
connection_state = ConnectionState.CONNECTED;
}
handler["notifyinitialized"] = handle_notify_initialized;
handler["notifyusercount"] = handle_notify_user_count;
}
}
}