aboutsummaryrefslogtreecommitdiffstats
path: root/src/server/Main.hx
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/Main.hx')
-rw-r--r--src/server/Main.hx251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/server/Main.hx b/src/server/Main.hx
new file mode 100644
index 0000000..8ec7e87
--- /dev/null
+++ b/src/server/Main.hx
@@ -0,0 +1,251 @@
+package server;
+
+import haxe.Timer;
+import Client.ClientData;
+import haxe.Json;
+import js.Node.process;
+import js.Node.__dirname;
+import js.npm.ws.Server as WSServer;
+import js.npm.ws.WebSocket;
+import js.node.Http;
+import js.node.Dns;
+import Types;
+using ClientTools;
+using Lambda;
+
+class Main {
+
+ final wss:WSServer;
+ final clients:Array<Client> = [];
+ final videoList:Array<VideoItem> = [];
+ final videoTimer = new VideoTimer();
+
+ static function main():Void new Main();
+
+ public function new(port = 4200, wsPort = 4201) {
+ wss = new WSServer({port: wsPort});
+ wss.on("connection", onConnect);
+ function exit() {
+ process.exit();
+ }
+ process.on("exit", exit);
+ process.on("SIGINT", exit); // ctrl+c
+ process.on("uncaughtException", (log) -> {
+ trace(log);
+ });
+ process.on("unhandledRejection", (reason, promise) -> {
+ trace("Unhandled Rejection at:", reason);
+ });
+
+ getPublicIp(ip -> {
+ trace('Local: http://127.0.0.1:$port');
+ trace('Global: http://$ip:$port');
+ });
+
+ final dir = '$__dirname/../res';
+ HttpServer.init(dir);
+ Lang.init('$dir/langs');
+
+ Http.createServer((req, res) -> {
+ HttpServer.serveFiles(req, res);
+ }).listen(port);
+ }
+
+ function getPublicIp(callback:(ip:String)->Void):Void {
+ Dns.resolve("google.com", function(err, arr) {
+ if (err != null) {
+ callback("ERROR " + err.code);
+ return;
+ }
+ Http.get("http://myexternalip.com/raw", r -> {
+ r.setEncoding("utf8");
+ r.on("data", callback);
+ });
+ });
+ }
+
+ function onConnect(ws:WebSocket, req):Void {
+ final ip = req.connection.remoteAddress;
+ trace('Client connected ($ip)');
+ final client = new Client(ws, "Unknown", false);
+ clients.push(client);
+
+ send(client, {
+ type: Connected,
+ connected: {
+ isUnknownClient: true,
+ clientName: client.name,
+ clients: [
+ for (client in clients) client.getData()
+ ],
+ videoList: videoList
+ }
+ });
+ sendClientList();
+
+ ws.on("message", data -> {
+ onMessage(client, Json.parse(data));
+ });
+ ws.on("close", err -> {
+ trace('Client ${client.name} disconnected');
+ clients.remove(client);
+ sendClientList();
+ if (client.isLeader) {
+ if (videoTimer.isPaused()) videoTimer.play();
+ }
+ });
+ }
+
+ function onMessage(client:Client, data:WsEvent):Void {
+ switch (data.type) {
+ case Connected:
+ case UpdateClients:
+ sendClientList();
+ case Login:
+ final name = data.login.clientName;
+ if (name.length == 0 || name.length > 20 || clients.getByName(name) != null) {
+ send(client, {type: LoginError});
+ return;
+ }
+ client.name = data.login.clientName;
+ send(client, {
+ type: data.type,
+ login: {
+ isUnknownClient: true,
+ clientName: client.name,
+ clients: clientList()
+ }
+ });
+ sendClientList();
+ case LoginError:
+ case Logout:
+ final oldName = client.name;
+ client.name = "Unknown";
+ send(client, {
+ type: data.type,
+ logout: {
+ clientName: oldName,
+ clients: clientList()
+ }
+ });
+ sendClientList();
+ case Message:
+ // todo message log, max items
+ // todo message max length check
+ data.message.clientName = client.name;
+ broadcast(data);
+ case AddVideo:
+ videoList.push(data.addVideo.item);
+ broadcast(data);
+ if (videoList.length == 1) {
+ waitVideoStart = Timer.delay(startVideoPlayback, 3000);
+ }
+ case VideoLoaded:
+ prepareVideoPlayback();
+ case RemoveVideo:
+ if (videoList.length == 0) return;
+ final url = data.removeVideo.url;
+ if (videoList[0].url == url) videoTimer.stop();
+ videoList.remove(
+ videoList.find(item -> item.url == url)
+ );
+ broadcast(data);
+ case Pause:
+ if (videoList.length == 0) return;
+ if (!client.isLeader) return;
+ videoTimer.pause();
+ broadcast(data);
+ case Play:
+ if (videoList.length == 0) return;
+ if (!client.isLeader) return;
+ videoTimer.play();
+ broadcast(data);
+ case GetTime:
+ if (videoList.length == 0) return;
+ if (videoTimer.getTime() > videoList[0].duration) {
+ videoTimer.stop();
+ onMessage(client, {
+ type: RemoveVideo,
+ removeVideo: {
+ url: videoList[0].url
+ }
+ });
+ return;
+ }
+ send(client, {
+ type: GetTime, getTime: {
+ time: videoTimer.getTime(),
+ paused: videoTimer.isPaused()
+ }});
+ case SetTime:
+ if (videoList.length == 0) return;
+ if (!client.isLeader) return;
+ videoTimer.setTime(data.setTime.time);
+ broadcastExcept(client, data);
+ case SetLeader:
+ clients.setLeader(data.setLeader.clientName);
+ sendClientList();
+ if (videoList.length == 0) return;
+ if (!clients.hasLeader()) {
+ if (videoTimer.isPaused()) videoTimer.play();
+ broadcast({
+ type: Play, play: {
+ time: videoTimer.getTime()
+ }
+ });
+ }
+ }
+ }
+
+ function clientList():Array<ClientData> {
+ return [
+ for (client in clients) client.getData()
+ ];
+ }
+
+ function sendClientList():Void {
+ broadcast({
+ type: UpdateClients,
+ updateClients: {
+ clients: clientList()
+ }
+ });
+ }
+
+ function send(client:Client, data:WsEvent):Void {
+ client.ws.send(Json.stringify(data), null);
+ }
+
+ function broadcast(data:WsEvent):Void {
+ final json = Json.stringify(data);
+ for (client in clients) client.ws.send(json, null);
+ }
+
+ function broadcastExcept(skipped:Client, data:WsEvent):Void {
+ final json = Json.stringify(data);
+ for (client in clients) {
+ if (client == skipped) continue;
+ client.ws.send(json, null);
+ }
+ }
+
+ var waitVideoStart:Timer;
+ var loadedClientsCount = 0;
+
+ function prepareVideoPlayback():Void {
+ if (videoTimer.isStarted) return;
+ loadedClientsCount++;
+ if (loadedClientsCount == 1) {
+ waitVideoStart = Timer.delay(startVideoPlayback, 3000);
+ }
+ if (loadedClientsCount >= clients.length) startVideoPlayback();
+ }
+
+ function startVideoPlayback():Void {
+ if (waitVideoStart != null) waitVideoStart.stop();
+ loadedClientsCount = 0;
+ broadcast({type: VideoLoaded});
+ videoTimer.start();
+ }
+
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage