From cec5fca5c4396940f6fe1aaa589309c9b78ca22c Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 27 Aug 2020 11:10:00 +0200 Subject: [PATCH] JSON serial change & working basic gameplay --- client/src/components/Game.vue | 38 +++++++------- client/src/store/gameModule.js | 9 ++-- client/src/store/socketPlugin.js | 2 +- server/.factorypath | 2 +- server/pom.xml | 6 +++ .../gltronic/tronio/TronIoApplication.java | 16 ++++++ .../gltronic/tronio/business/GameManager.java | 49 +++++++++++++----- .../gltronic/tronio/business/SocketUtils.java | 9 ++-- .../java/gltronic/tronio/model/Player.java | 21 +++++--- .../gltronic/tronio/web/SocketHandler.java | 12 +++-- .../gltronic/tronio/TronIoApplication.class | Bin 952 -> 1549 bytes .../tronio/business/GameManager.class | Bin 6949 -> 8071 bytes .../tronio/business/SocketUtils.class | Bin 2924 -> 2769 bytes .../gltronic/tronio/model/Player.class | Bin 2078 -> 2202 bytes .../gltronic/tronio/web/SocketHandler.class | Bin 3152 -> 3337 bytes test.json | 19 +++++++ 16 files changed, 131 insertions(+), 52 deletions(-) create mode 100644 test.json diff --git a/client/src/components/Game.vue b/client/src/components/Game.vue index cbe50fd..87f50ef 100644 --- a/client/src/components/Game.vue +++ b/client/src/components/Game.vue @@ -53,13 +53,13 @@ export default { this.players.forEach(player => { this.renderPlayer(player) - this.renderWalls(player.walls, player.color) + this.renderWalls(player) }) - this.renderDebug() + this.renderDebug(this.player) - this.checkPlayerColision() - this.updatePlayer() + // this.checkPlayerColision() + this.updatePlayer(this.player) }, renderBorders () { this.context.strokeStyle = 'black' @@ -80,19 +80,19 @@ export default { this.context.restore() }, - renderWalls (walls, color) { + renderWalls (player) { this.context.beginPath() this.context.lineWidth = this.settings.wallSize - this.context.strokeStyle = color - walls.forEach(wall => { + this.context.strokeStyle = player.color + player.walls.forEach(wall => { const canvasX = this.canvas.width / 2 + wall.x - this.player.x const canvasY = this.canvas.height / 2 + wall.y - this.player.y this.context.lineTo(canvasX, canvasY) }) - this.context.lineTo(this.canvas.width / 2, this.canvas.height / 2) + this.context.lineTo(this.canvas.width / 2 + player.x - this.player.x, this.canvas.height / 2 + player.y - this.player.y) this.context.stroke() }, - renderDebug () { + renderDebug (player) { this.context.beginPath() const canvasX = this.canvas.width / 2 const canvasY = this.canvas.height / 2 @@ -102,8 +102,8 @@ export default { this.context.strokeStyle = 'blue' this.context.stroke() - const x = canvasX + 30 * Math.cos(this.player.angle) * 2 - const y = canvasY + 30 * Math.sin(this.player.angle) * 2 + const x = canvasX + 30 * Math.cos(player.angle) * 2 + const y = canvasY + 30 * Math.sin(player.angle) * 2 this.context.beginPath() this.context.lineTo(canvasX, canvasY) this.context.lineTo(x, y) @@ -125,10 +125,10 @@ export default { this.context.strokeStyle = 'purple' this.context.stroke() - this.context.fillText('player x: ' + this.player.x + ' y:' + this.player.y, 10, 10) - this.context.fillText('a:' + this.player.angle + ' a_t' + this.player.targetAngle, 10, 20) + this.context.fillText('player x: ' + player.x + ' y:' + player.y, 10, 10) + this.context.fillText('a:' + player.angle + ' a_t' + player.targetAngle, 10, 20) this.context.fillText('canvasX: ' + canvasX + ' canvasY:' + canvasY, 10, 30) - this.context.fillText('walls: ' + this.player.walls.length, 10, 40) + this.context.fillText('walls: ' + player.walls.length, 10, 40) }, mouseEvent (event) { var rect = this.canvas.getBoundingClientRect() @@ -141,17 +141,19 @@ export default { this.player.targetAngle = Math.atan2(dy, dx) send({ message: this.player, type: 'update' }) }, - updatePlayer () { + updatePlayer (player) { + /* this.player.lastWall++ if (this.player.lastWall > this.settings.wallUpdate) { this.player.walls.push({ x: this.player.x, y: this.player.y }) this.player.lastWall = 0 } + */ - this.player.x += this.settings.playerSpeed * Math.cos(this.player.angle) - this.player.y += this.settings.playerSpeed * Math.sin(this.player.angle) + player.x += this.settings.playerSpeed * Math.cos(player.angle) + player.y += this.settings.playerSpeed * Math.sin(player.angle) - this.player.angle = this.player.targetAngle + player.angle = player.targetAngle // angle lag /* if (this.player.targetAngle !== this.player.angle) { diff --git a/client/src/store/gameModule.js b/client/src/store/gameModule.js index 364f7df..937f3bf 100644 --- a/client/src/store/gameModule.js +++ b/client/src/store/gameModule.js @@ -4,8 +4,6 @@ const state = { y: 100, angle: 0, targetAngle: 0, - walls: [], - lastWall: 0, color: 'red' }, players: { @@ -37,8 +35,10 @@ const actions = { commit('SET_SETTINGS', settings) }, update ({ commit }, update) { + commit('SET_PLAYERS', update) }, - dead ({ commit }) { + dead ({ commit }, player) { + commit('SET_PLAYER', player) }, error ({ commit }, error) { alert('Error: ' + error) @@ -49,6 +49,9 @@ const mutations = { SET_PLAYER (state, player) { state.player = player }, + SET_PLAYERS (state, players) { + state.players = players + }, SET_SETTINGS (state, settings) { state.settings = settings }, diff --git a/client/src/store/socketPlugin.js b/client/src/store/socketPlugin.js index cc36243..73a0f45 100644 --- a/client/src/store/socketPlugin.js +++ b/client/src/store/socketPlugin.js @@ -33,7 +33,7 @@ export default function createSocketPlugin () { store.dispatch('game/update', data.message) break case 'gamePlayerDead': - store.dispatch('game/dead') + store.dispatch('game/dead', data.message) break default: break diff --git a/server/.factorypath b/server/.factorypath index 30583c9..2e7aef0 100644 --- a/server/.factorypath +++ b/server/.factorypath @@ -14,7 +14,6 @@ - @@ -36,4 +35,5 @@ + diff --git a/server/pom.xml b/server/pom.xml index 7ca1408..60e8c9e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -50,6 +50,12 @@ json 20200518 + + com.fasterxml.jackson.core + jackson-core + 2.11.2 + + diff --git a/server/src/main/java/gltronic/tronio/TronIoApplication.java b/server/src/main/java/gltronic/tronio/TronIoApplication.java index 781e7e4..a14c5c4 100644 --- a/server/src/main/java/gltronic/tronio/TronIoApplication.java +++ b/server/src/main/java/gltronic/tronio/TronIoApplication.java @@ -2,10 +2,15 @@ package gltronic.tronio; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @SpringBootApplication +@EnableScheduling @ComponentScan(basePackages = "gltronic.tronio") @PropertySource("classpath:application.properties") public class TronIoApplication { @@ -14,4 +19,15 @@ public class TronIoApplication { SpringApplication.run(TronIoApplication.class, args); } + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + + scheduler.setPoolSize(2); + scheduler.setThreadNamePrefix("scheduled-task-"); + scheduler.setDaemon(true); + + return scheduler; + } + } diff --git a/server/src/main/java/gltronic/tronio/business/GameManager.java b/server/src/main/java/gltronic/tronio/business/GameManager.java index a2d51ae..7dab49a 100644 --- a/server/src/main/java/gltronic/tronio/business/GameManager.java +++ b/server/src/main/java/gltronic/tronio/business/GameManager.java @@ -4,8 +4,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Random; -import org.json.JSONObject; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.web.socket.WebSocketSession; @@ -24,14 +27,17 @@ public class GameManager implements IGameManager { SocketUtils.sendMessage(session, "error", "cant login twice"); return; } - + Player player = initPlayer(new Player()); game.getSessions().put(session.getId(), session); game.getPlayers().put(session.getId(), player); - SocketUtils.forwardMessage(session, "login", new JSONObject(player)); - SocketUtils.forwardMessage(session, "gameSettings", new JSONObject(game.getSettings())); + System.out.println("[GAME] Player " + session.getId() + " logged in | status: " + player.getX() + " " + player.getY() + " " + player.getColor()); + + SocketUtils.sendObject(session, "login", new ObjectMapper().writeValueAsString(player)); + SocketUtils.sendObject(session, "gameSettings", new ObjectMapper().writeValueAsString(game.getSettings())); + SocketUtils.sendObject(session, "gameUpdate", new ObjectMapper().writeValueAsString(game.getPlayers().values())); } @Override @@ -48,14 +54,16 @@ public class GameManager implements IGameManager { } @Override + @Scheduled(fixedDelay = 1000 / 60) public void step() throws InterruptedException, IOException { // CHECK OUT OF BORDERS & COLISIONS game.getPlayers().forEach((id, player) -> { // OUT OF BORDERS - if (player.getX() - game.getSettings().getPlayerSize() < 0 || - player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize() || - player.getY() - game.getSettings().getPlayerSize() < 0 || - player.getY() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) { + if (player.getX() - game.getSettings().getPlayerSize() < 0 + || player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize() + || player.getY() - game.getSettings().getPlayerSize() < 0 + || player.getY() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) { + System.out.println("[GAME] border"); killPlayer(id); return; } @@ -65,8 +73,12 @@ public class GameManager implements IGameManager { for (var i = 0; i < player2.getWalls().size() - 2; i++) { Wall wallA = player2.getWalls().get(i); Wall wallB = player2.getWalls().get(i + 1); - if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(), game.getSettings().getPlayerSize())) { - if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(), game.getSettings().getPlayerSize())) { + if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(), + game.getSettings().getPlayerSize())) { + System.out.println("[GAME] close to wall"); + if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), + player.getY(), game.getSettings().getPlayerSize())) { + System.out.println("[GAME] touch da wall"); killPlayer(id); return; } @@ -88,12 +100,21 @@ public class GameManager implements IGameManager { player.setX(player.getX() + game.getSettings().getPlayerSpeed() * Math.cos(player.getAngle())); player.setY(player.getY() + game.getSettings().getPlayerSpeed() * Math.sin(player.getAngle())); }); + + SocketUtils.broadcast(game, "gameUpdate", new ObjectMapper().writeValueAsString(game.getPlayers().values())); } - private void killPlayer (String id) { + private void killPlayer(String id) { Player player = game.getPlayers().get(id); initPlayer(player); - SocketUtils.sendMessage(game.getSessions().get(id), "gamePlayerDead", "yo dead"); + System.out.println("[GAME] Player " + id + " is dead | status: " + player.getX() + " " + player.getY() + " " + player.getColor()); + try { + SocketUtils.sendObject(game.getSessions().get(id), "gamePlayerDead", + new ObjectMapper().writeValueAsString(player)); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } private boolean isCloseToWall (double xa, double ya, double xb, double yb, double xc, double yc, double radius) { @@ -120,8 +141,8 @@ public class GameManager implements IGameManager { double x = game.getSettings().getArenaSize() * (0.25 + Math.random() * 0.5); double y = game.getSettings().getArenaSize() * (0.25 + Math.random() * 0.5); - player.setAngle(0); - player.setTargetAngle(0); + // player.setAngle(0); + // player.setTargetAngle(0); player.setLastWall(0); player.setWalls(new ArrayList()); player.setColor(String.format("#%06x", rand.nextInt(0xffffff + 1))); diff --git a/server/src/main/java/gltronic/tronio/business/SocketUtils.java b/server/src/main/java/gltronic/tronio/business/SocketUtils.java index d070d46..0fb79cd 100644 --- a/server/src/main/java/gltronic/tronio/business/SocketUtils.java +++ b/server/src/main/java/gltronic/tronio/business/SocketUtils.java @@ -2,7 +2,6 @@ package gltronic.tronio.business; import java.io.IOException; -import org.json.JSONObject; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; @@ -17,17 +16,17 @@ public class SocketUtils { } } - public static void forwardMessage(WebSocketSession session, String type, JSONObject message) { + public static void sendObject(WebSocketSession session, String type, String object) { try { - session.sendMessage(new TextMessage("{\"type\":\"" + type + "\",\"message\": " + message.toString() + " }")); + session.sendMessage(new TextMessage("{\"type\":\"" + type + "\",\"message\": " + object + " }")); } catch (IOException e) { e.printStackTrace(); } } - public static void broadcast(Game game, String type, JSONObject message) { + public static void broadcast(Game game, String type, String message) { game.getSessions().forEach((id, session) -> { - forwardMessage(session, type, message); + sendObject(session, type, message); }); } } \ No newline at end of file diff --git a/server/src/main/java/gltronic/tronio/model/Player.java b/server/src/main/java/gltronic/tronio/model/Player.java index 7b4150c..64ee2d3 100644 --- a/server/src/main/java/gltronic/tronio/model/Player.java +++ b/server/src/main/java/gltronic/tronio/model/Player.java @@ -2,6 +2,8 @@ package gltronic.tronio.model; import java.util.ArrayList; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -9,13 +11,20 @@ import lombok.Setter; @Getter @Setter @NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) public class Player { - private double x; - private double y; - private double angle; - private double targetAngle; - private ArrayList walls; - private int lastWall; + private String color; + private double x; + + private double y; + + private double angle; + + private double targetAngle; + + private ArrayList walls; + + private int lastWall; } \ No newline at end of file diff --git a/server/src/main/java/gltronic/tronio/web/SocketHandler.java b/server/src/main/java/gltronic/tronio/web/SocketHandler.java index 1d2222f..6e7bec8 100644 --- a/server/src/main/java/gltronic/tronio/web/SocketHandler.java +++ b/server/src/main/java/gltronic/tronio/web/SocketHandler.java @@ -2,6 +2,8 @@ package gltronic.tronio.web; import java.io.IOException; +import com.fasterxml.jackson.databind.ObjectMapper; + import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -23,15 +25,17 @@ public class SocketHandler extends TextWebSocketHandler { @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException { - System.err.println("[WS] message :" + message.getPayload()); + // System.err.println("[WS] message :" + message.getPayload()); String payload = message.getPayload(); - JSONObject jsonObject = new JSONObject(payload); + JSONObject rootObject = new JSONObject(payload); - String type = (String) jsonObject.get("type"); + String type = rootObject.getString("type"); switch (type) { case "update": - gameManager.updatePlayer(session, (Player) jsonObject.get("message")); + Player player = new ObjectMapper().readValue(rootObject.get("message").toString(), Player.class); + + gameManager.updatePlayer(session, player); break; default: SocketUtils.sendMessage(session, "error", "unknow command"); diff --git a/server/target/classes/gltronic/tronio/TronIoApplication.class b/server/target/classes/gltronic/tronio/TronIoApplication.class index 43c49f2e630d888edb3989c201668788288d1981..6bd2fe1870afb41872c73fd98a4a9d578b30248f 100644 GIT binary patch delta 600 zcmb7?-AWrl6vzK(k~F$mskka?Yufs`n{|y=Yd=z2`gI|Q2$6sa!kUa4OlD!Tf_UQ- z6qI=YZ@siPX=$Mk&vsT8@#2$pMNVH5 zwvNixLs>g+$e6I;diq=pjkaz>b#+kO>{gWYPs&rk8aMY=hN_fBp72Irh{3b2nrft~ z$8juGQrtX@WmwyYqQ*ZU3*+cD=8=sFOcHV}nHW{MekuuRch#%(V486EH&)gHbJoDr z%-V3_5ge|$<*$d$oZN`ze*J_n*}=|r>ff$?3=9nB49Xk1RxnP^U{;fr3@Xhl$;?d+%Ph`J%1L$1%gZlGEXmBz zD`sS{n|y&;bn+5rZALRNtByr@vN_9qMyAQvS+p3rCo{3yv+^;pGw@G#W0lioVvq!y l&BP$ZAPuDD7?>GkfV3VW3)RggH}7s>@1S7 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 literal 6949 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 diff --git a/server/target/classes/gltronic/tronio/business/SocketUtils.class b/server/target/classes/gltronic/tronio/business/SocketUtils.class index df04686137828f522048ccebf95120f8c59b0770..6619551de5f655f8c9e5720fda3732beb2d097be 100644 GIT binary patch delta 765 zcmZ8eJ5v);6#njJvv;%E7?MaBm;jkLBbWq2Fh0OXR76DtR1}Rb+=La0A&G3Hah&`H z=hhZh78XvLAfT2OcDCBsSm|$YJa>&2ZgJ1K=brDe=lqO*?lwOC+W7$B2pTQpy(e!t zc6DW;ShhSmd}$ z`ekd=$~7#}LhTA4iA}F3}lbVGRM^Wr* zL!K4b;=A^Z-4`EuI=Ctu%O-B35Y(_Hm_8e+Fqkvds=YB)u^h*CIO<|qPpTUX10u_N zM38gwTF)~>{LnW80gPe~#u3C}gha;JC!YJpn?WexG|v~%pi;~wchObYMYs^&yM<_~ zjoxscjTToQKnG4ib)(JqT|%Sp;S4tDo)TP;31$~J*XG(;Vh;*_7|z&L$y${ zjdQ8=4lV)PxU8T>MaA`GPy}M=!l3IVE&)x1u!I}*K-hm?LYQ#9bVQfvQHo9gU4~o% z8J0846N8)?A4#U(;@1CC{WQ`6S1RgCbKG%R0j;t|$S NrHb5o4UgfV_cvTdd6@tJ delta 741 zcmZuv%Wl&^6g?BW9^*PqNoZ(`NJFARX+tb3@oEJDO5aIS2rZBDsN=XHPX{|q*`V?d zv*atfiJ%gE01G5GtdZFA1uPKP?JlY;&AoGd&OPVO%#WczQSIBm!><5t;N5}tMT!jU zIPRY5mU6b|nPr<{YeE>(RUxy8_`v1;s^>I~l|p_kU#!|zd-n3CihB&fL@LoZrX*(K z8s?DTc)$>G+_KTbQL(@fh`(nT%AW9hmf=*P%SqqI zXirb_p3;{{%Z6f{JIuSQ!8`f{^ryK{BZ^K6fUjE;+KWnAItFvKX0iHMqE{i2|D zf&)k+glP=oI>O?oy1_D{rY3`TyBJAzai2nXUVK#3!6+fm5^|W3ql8SdMe$cP_Ls1X z065)y0TO+{>B%lurc#Gk2R`F*00&eIK%-To2%IOmIQl7z9~1B#TeKr8J)WC1m7m?h zBB5_n9+U{+zbjA?gPK1vfqczOjXKDb@GFy3pYW?Kcb zI;w**f_m5FM`?S~uh~ym*PN;rl&uC{pak_*+GopJxqfY0kobge0pBJJ;pI7k3R%zPB9+VgmNKK? zyU66kKLQHNh=4MP;FX|sacqHaIbl%;9SGI0S#ipuJS#X-QzcfOv8a=k-B3Pn7m$z6 zTU6M#=R6U^k?qX()pH{Lhxg0cA)md~6??VDy;xQ34 z_^jz`x9+UC4Hp!e^Zh_e?FK%oF;%Gsb^9G^?5Iz5&t8+&4~@XLWn^SO!oFPd1LZ8K zVBJyLbsC^n7lsA87ybu7*znVsZO{oHuQ1|FXRK08I3vL`BS9@A!6PHlXn1v-br0;( zTC97Z>!U;XAI8px6p-1{ck2IYOpoB7Gs*%T#o7uDXpoL!C+Ij0;U7Au!ZSDze@Wzg zGS+z{ne*vb=h0-&XJehuC37y)7z!Acvb3*o(NE{1y!-g=GFkkR1kc zG9GdyDI`-lg3KGpsd&iIq>wxn5oD)gKGDS@Pp>| aB6h{R2q~I399*FLkOjaj;(PFh9{vSxHdVX; literal 2078 zcma)6TTc^F5T4WfWhsahP!I$!v_)BOfC8cjnvkqY0zr9P%BCz?x@Nmo`=3nIL}KED zKfoVl{N|i?TW|^cuzM~u^UcgRGw09WU%!cHkzS@LCTP6o_?}z0tGZlV{n$OQ9DU0% zTb7rmgrI>V^P{OdX1%7r-9NIbz98`#-v+*xpdv*GgzVQeCds_VF(pg{#D z!`=2hyIzBC0)f336fcz#bYdMDW=YVnMoCHu>No7V^`?2eZ+ScBKIbxMxK-2HH9ecp z?aPFJXd|PEa{^P0wVmxT{xyx#jGyVy!k5=I%J5|Ympfg?Sad_9Y)79HgR_xRIgA{5 znGcs-KpVR$X_TWp3Wf-bfJ1Pz$bUA9m38lFG(eZYXhYJQC~cH? z(J!wp|Ij_yGClLy@+}W(u)-cNv9$Z6fNf9uO4d$#v=o{%V=sh?E=ZaUhfi0u^`TSM zJOb&mkSJ}kHV4{HzS#EIn>(wdR_wa%uga#{%~70QX!MMp3(9S~O|NRbvgJ^yu9)K? zpeaxm$LL}1xbd)yaf8SeJt0>#fLu`_Xlruc2R+n8?!h5vPo$;uBZ zewAt?_^0G$mPWDHlmcC$t2hZ73pE~(W;_vUJQ>Y+D%5y7n(g(n&r4_oZiQ53=7fsh&$!OXnhisj ztEg(mJ8l|0m$*&h!NcqcdK3;h9u<=NFMv!-$oX){$*7Rr$^m3XLN0_uPDh30sR$sm z5^|9qgF)2_Pb8-X_>zoOW>08_q2oQha2}^>(3YUBW+a8jv9IeaC!HI3tld%(; xjT%2sZxDY$LO!L{{~!^!9u<-wf&g+rLgM+s)#=9mNo!{j^Pw-oIv9x2#y`GBE+7B^ diff --git a/server/target/classes/gltronic/tronio/web/SocketHandler.class b/server/target/classes/gltronic/tronio/web/SocketHandler.class index 9dfba346846f07d729d49462d267d09088dfdc46..888b7908be01d555895eeb3331956380f686c280 100644 GIT binary patch delta 1366 zcmY*ZZBrXn6n^gRCN~KSEomn;fFuE=K){9C0u59x6s0dfYg%e7s7tayV6z*Og@SMO zt=iVN*6YiRU;O0MnL6XNI*XOj=~s28`a>K)cl@B9O{}!DlY7qIJ@+}!dCs}{{eg=O zpQdTMfNZpp|xAtc0CQY;?8ZU)6!FB-IiP8D59G6WY4rwSmpDJ`NX`}Yim z5JQiOhoLb97WMMtsGTzyEE=T{`cWG|ih&o5a#>%f5F1u;0FR2G(%1YrMmP@M zS8MgWicuUA7vvT{#}f=q;()Iy>cNexIE*7yRmsqE)4I7rv@eYcPr998=;~rAt16r^Fp)zT+f=&nT7bQUKEobt~4gWuJ{x!3EvQ zh43_<;m9zwiQ7t4eBrB=&tOJ;#XzJ@miBTJeea#Vu+O$IoksK%W zIn&@+WY9!?KofO5KTx24Zm}L)%Nj)|Z(Bjwc!8sMpDyWjlDwdzgtC~H;>{}zySC0& z8$7a-H**HDKP#^AXv9HV2o7FU@e*F9rg}ZGa^eQ>9rpG|LO6%lIbOTVB5!NQY0$iB zF?34x1|Fi$6}>If@&tQJ#d$1?Klu5*7a1aFPN!!{9m}E_B--(;X_t+h7x=D<_pnTA znufkgO*ip@iVwg2GT>)yr}#RUJl6^t0lEcFw~Od4#3nb9$nJRt2*z-C3SJAVH{>%mj>C}D~LoOZ*kGXjA7RFM%>pL7x zM)^%l$heA{WH=o@^*v5s2Nf=(j-c8Rq_vr-#1N)S(})%vL>p%4%OZAT8J#F2iER`$ z4o<6s?WiZ~d9!$y2FmkNcn%Vwn0OvKVTUnCPZkOxW(Y^8nVTV$91KF4r~GJz@(*1t z&cQh7ayM|qI1-gL2B@8XQDmObgAyeIr62{cM3iLgBwwL76E)<`k}r`>+<>(SClg-X zz*?7!br-L=c(tO!O}r_A3a3D??)f@G0W?I=i#Yl!1xBhU{}-6}uRxE4k5x!8yp4A# z%u9QjtU|Vt(uOZ=CAuV$*n!C&7w?N7YvT3$NS6UpWRQ~Wr$4(P@n_Ahqj$AyB%CXR l6d|mSAc)pHVRA^PI85bU0WQX=+Vfzfcfc>IXY3~bDw}&_e7`6sIma5iyLi6%o&9SM? zEIJvu>$}Poj>F7Gb({_FOc(vd# z$a}j_>o`q~&ASELqnr$0F>n^=#AUfH76QYF0Tr5#3si1{1dhBIQW8x~7{f)5*BCm* zO*Jb1P$Kf{7#DvkSBy&xs#B{~YdR)`t!~vW!{&HfTvxm0Nlb~))h7cp^=f5ga>kkR z7`C6S){2(zvCia2hS%LWhD~|Tp1OL@c1P{WvcoaUV2L3qArgEkR(r}$aDyiorySQS zRVz9?u}P2W2`u=$UlWJ*zOdiyl7Z`R#TWW2yCr%<@s=^%Hn0Mhnk_r_0?j{%_YA!M zjUCb$GsSc`z1#%_oVI7>YeXL+F>bcqM@OcCt{*|Nw}IyyIIwXZeu&(7&wad@RicVH z*uYT=%BoRyJgYTu;!A?T0s7f*82FjA8P&{}8#wbBv^B(1-(omTdFO+?S07+h#@aHP z(&iiHo8RHBdz2v~Lb3}XlC;J2?WUFOrRB*Y2^;O0M+X+siQBZG&r#GMG?FXWj4h;n zs{n^C*0(O;3M4|9KoQe~-HSX*n4yWYIE5qRQkWb{t(F!Y`?^pd`Z$yh9m&kxxl0q~lb?yh-=70lO?=4f=3T1Mi9j zBU$f