public void EnqueueOnConnect(string connectionId, string token) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "connectionIdとplayerDataが揃った状態でくる。ので、ここで照会を行ってしまおう。token:" + token); var playerId = token; XrossPeer.Log("playerId:" + playerId + " connectionId:" + connectionId); // set connectionId to reserved playerId. var succeeded = gameLayer.SetConnectionIdOfPlayerId(playerId, connectionId); if (!succeeded) { return; } if (playerId == "_empty_") { XrossPeer.Log("接続時にプレイヤーIDが空のユーザーが接続してきた。追い返すようにしてある。"); return; } var data = new Commands.OnConnected(playerId).ToData(); if (true) { gameLayer.EnqueOnReceive(connectionId, data); } }
/* * C#のtickで動くThread。 * UnityEditorのUpdateと違い、UnityPlayerと同期しないため、 * UnityPlayer側が重くなっても構わず動く。 * * で、UnityPlayer側が遅れてかつUnityServer側が動くと、unsyncエラーが多発する。 * このUnity内でのUnityPlayerと合わせてUnityEditor上で使うには都合が悪い。 * * 別のUnityPlayerに対してのServerインスタンスとしては行けると思う。 * あとはCoreCLRにするとか。 */ public Updater(string loopId, Func <bool> OnUpdate) { var framePerSecond = RolePlayingChatDefinitions.FRAMERATE; var mainThreadInterval = 1000f / framePerSecond; continuation = true; Action loopMethod = () => { try { double nextFrame = (double)System.Environment.TickCount; var before = 0.0; var tickCount = (double)System.Environment.TickCount; while (true) { tickCount = System.Environment.TickCount * 1.0; if (nextFrame - tickCount > 1) { // XrossPeer.Log("wait:" + (int)(nextFrame - tickCount)); Thread.Sleep((int)(nextFrame - tickCount) / 2); /* * waitを半分くらいにすると特定フレームで安定した。 */ continue; } if (tickCount >= nextFrame + mainThreadInterval) { nextFrame += mainThreadInterval; continue; } if (!continuation) { break; } // run action for update. continuation = OnUpdate(); if (!continuation) { break; } nextFrame += mainThreadInterval; before = tickCount; } XrossPeer.Log("loopId:" + loopId + " is finished."); } catch (Exception e) { XrossPeer.LogError("loopId:" + loopId + " error:" + e); } }; var thread = new Thread(new ThreadStart(loopMethod)); thread.Start(); }
public void EnqueueOnMessage(string connectionId, byte[] data) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "とりあえずすべてのconnectionIdに対して、このreserveレイヤに登録があった、みたいなみなしをしてうけいれる。実際にはReservationLayerが複数のGameLayerをもっていて、特定の情報を元にGameContextLayerへとメッセージをふり分ける。"); if (true) { gameLayer.EnqueOnReceive(connectionId, data); } }
/** * main loop of GameContext. */ public bool UpdateGameLayer() { switch (state) { case BattleState.STATE_READY: { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "絶賛準備中のステート、2フレームくらい回してみよう。clientのリトライを試す、、のはあとで。"); if (true) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "Server起動中、準備中など。いろんなConnectionとかロードが完了したらプレイヤーがいない世界へ遷移"); state = BattleState.STATE_NOPLAYERS; } break; } /* * ゲームが稼働しているステート */ case BattleState.STATE_NOPLAYERS: case BattleState.STATE_PLAYERS_EXISTS: { // worldにいるユーザー = dummyの挙動を開始する。 world.UpdateWorld(gameFrame, StackPublish); UpdateXrossPeer(gameFrame); gameFrame++; break; } case BattleState.STATE_ENDING: { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "とりあえずリセットをかける。クライアント側にはサーバリセット後の通信が届くので、そこから勝手に復帰させるといい感じになる。"); // 時間稼ぎとか。UniRxで10秒とかを計ると良いんだと思う。 STATE_ENDED state = BattleState.STATE_ENDED; break; } case BattleState.STATE_ENDED: { // do nothing yet. return(false); //SAYONARA!!! } default: { XrossPeer.Log("message received at missing state:" + state); break; } } /* * send stacked data to players. */ PublishStackedData(); return(true); }
public void Setup(Action <string, byte[]> Send) { XrossPeer.Log("server ready:" + serverContextId); XrossPeer.TimeAssert(Develop.TIME_ASSERT, "リセットを兼ねることはしない方が良いんだろうか。"); // 仮の、ゲームに参加するconnectionIdを保持しておくレイヤ reservationLayer = new ReservationLayer(Send); this.Send = Send; }
public ServerInitializer() { XrossPeer.SetupLog(Path.Combine(Directory.GetParent(Application.dataPath).ToString(), "server.log")); // Setup(); DisquuunTests.Start(); EditorApplication.playmodeStateChanged += DetectPlayStart; EditorApplication.update += DetectCompileStart; }
/** * 文字列のパターンによって、AIにお願いをすることができる。 */ private void GenerateAnswer(string dummyPlayerId, string senderPlayerId, string message, string senderConnectionId) { XrossPeer.Log("この時点で、このAIが忙しくなければ、っていう判断をしてもいいかもしれない。localとremoteのAutoのあり方をどうするかな。"); if (!message.EndsWith("?")) { StackPublish(new Commands.Messaging(dummyPlayerId, senderPlayerId, "村人:" + dummyPlayerId + ":" + message + " って何?"), new string[] { senderConnectionId }); return; } // ?で終わってる場合、Queryとみて、動作開始。 // 今回は問答なしで、追いかけて行って云々っていう感じにする。まずカメラ引いてしまおう。 var ourIds = new List <string> { dummyPlayerId, senderPlayerId }; var anotherTargetId = world.ExceptPlayerIds(ourIds)[0]; StackPublish(new Commands.Messaging(dummyPlayerId, senderPlayerId, "村人_" + dummyPlayerId + ":" + "お、わかった〜、" + anotherTargetId + "に、\"" + message.Substring(0, message.Length - 1) + "\" って伝えとく。"), new string[] { senderConnectionId }); // メッセージを保持、実際にターゲットに向かって歩いてく。近づいて行って、最終的にメッセージを伝える。 var reservedMessage = "あのね〜 " + senderPlayerId + "からの伝言で、" + "\"" + message + "\"" + "ってさ。"; XrossPeer.Log("reservedMessage:" + reservedMessage); // サーバ側でAutoをどうやって組もうかな。 /* * ・サーバ側でのAutoを持つ * ・Autoを初期化する * ・Autoを適当にスタックする(デフォルト状態もAutoとして持った方がいいのかな。) * ・Autoからの発信を行う * とかか。全部協調動作する必要ないんで、サーバ側で適当なフレームで動かしつつ、時間がきたら実行、っていう感じかな。 * * Queryの部分をAutoで積む? * ・何時頃 とか * ・何回 とか * ・どのくらいしつこく とか */ world.StackAutoName(dummyPlayerId, "DoStalk", new List <string> { anotherTargetId, reservedMessage }); // 付け回して、相手にメッセージ投げて、それが済んだら world.StackAutoName(dummyPlayerId, "DoNotify", new List <string> { senderPlayerId, anotherTargetId, reservedMessage }); // 終わったよーって通知を出す world.StackAutoName(dummyPlayerId, "DoStalk", new List <string> { senderPlayerId, reservedMessage }); // メッセージを渡したやつに会いに行く var playerContext = world.GetPlayerInfo(dummyPlayerId); var newAuto = new DoOrder <PlayerContext, List <PlayerContext> >(gameFrame, playerContext); world.SetAuto(dummyPlayerId, newAuto, gameFrame); }
public void StackAutoName(string playerId, string autoName, List <string> parameters) { var playerContext = GetPlayerInfo(playerId); if (playerContext == null) { XrossPeer.Log("対象のplayer:" + playerId + " nullだったのでstackに失敗"); return; } playerContext.stackedDummyAutos.Add(new AutoInfo(autoName, parameters)); }
public void SetAuto(string playerId, Auto <PlayerContext, List <PlayerContext> > newAuto, int frame) { var playerContext = GetPlayerInfo(playerId); if (playerContext == null) { XrossPeer.Log("対象のplayer:" + playerId + " nullだったのでstackに失敗"); return; } playerContext.auto = newAuto; }
public bool DiscardConnectionIdOfPlayerId(string playerId) { var playerSlotIndex = Array.FindIndex(connections, connection => connection.playerId == playerId); if (-1 < playerSlotIndex) { connections[playerSlotIndex].connectionId = string.Empty; return(true); } XrossPeer.Log("failed to discard connectionId to playerId:" + playerId); return(false); }
public bool SetConnectionIdOfPlayerId(string playerId, string connectionId) { var playerSlotIndex = Array.FindIndex(connections, connection => connection.playerId == playerId); if (-1 < playerSlotIndex) { connections[playerSlotIndex].connectionId = connectionId; return(true); } XrossPeer.Log("failed to set connectionId to playerId:" + playerId); return(false); }
/* * enque data on receive. */ public void EnqueOnReceive(string connectionId, byte[] data) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "接続者が他人のplayerIdでデータを送ってきた場合、データを展開した瞬間にバレさせることができる。一度発見したら強制切断しよう。"); var playerId = PlayerIdFromConnectionId(connectionId); if (string.IsNullOrEmpty(playerId)) { XrossPeer.Log("不明なconnectionIdからのデータ:" + connectionId + " 内容は、:" + Encoding.UTF8.GetString(data)); return; } lock (gameDataQueue) gameDataQueue.Enqueue(new DataPack(playerId, data)); }
public void Setup() { XrossPeer.Log("\n\n"); XrossPeer.Log("----------"); XrossPeer.Log("initializing server context...."); XrossPeer.Log("----------"); var settings = (StandardAssetsConnectorSettings)ScriptableObject.CreateInstance("StandardAssetsConnectorSettings"); sContext = new ServerContext(settings.ClientToContextKey()); transformLayer = new ConnectionServerTransformLayer(settings.ClientToContextKey()); transformLayer.SetContext(sContext); }
public void Teardown() { XrossPeer.Log("\n\n"); XrossPeer.Log("----------"); XrossPeer.Log("teardown server context...."); XrossPeer.Log("----------"); if (sContext != null) { sContext.Teardown(); } if (transformLayer != null) { transformLayer.Disconnect(); } }
public ReservationLayer(Action <string, byte[]> publish) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "とりあえず通過できるtokenとして特定のplayerIdを直書きしてある。"); var reservedPlayerIds = new List <string> { "_empty_", }; /* * acceptable id is 100 ~ 199 */ for (var i = 100; i < 200; i++) { reservedPlayerIds.Add(i.ToString()); } reservationLayerId = Guid.NewGuid().ToString(); gameLayer = new GameContextLayer(reservedPlayerIds, publish); }
public void OnConnected(string connectionId, byte[] data) { XrossPeer.Log("OnConnected!"); /* * 接続時にidentityを確立する手段が2つ考えられて、 * 1.接続時にconnectionServer側で予約と付き合わせてなんとかする * 2.このレイヤーで予約と付き合わせてなんとかする * 責務分解的には、接続してきたらすぐキャッシュと照合、っていうので良い気はするんだけど。 * どっちにしてもconnectedで情報が必要なので、reservationレイヤーでそれを受け止めるのは悪くない。 * * ServerContextはゲームに集中させたい。 * ConnectionServerはコネクションに集中させたい。 * うーーん、、別のContextがあってそっちにつなぎにいけばいいのか。domain的にはConnection側だな、、 */ var playerIdString = Encoding.UTF8.GetString(data); if (reservationLayer != null) { reservationLayer.EnqueueOnConnect(connectionId, playerIdString); } }
public void EnqueueOnDisconnect(string connectionId, string token, string reason) { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "tokenそのまま使ってるんで、このままの構造だと、他人が偽って他プレイヤーの通信切断できちゃうな。プレイヤーしかしらないパラメータを使ってplayerIdを読みだす仕組みをつくらんとな。 具体的にはtokenが"); var playerId = token; var data = new Commands.OnDisconnected(playerId, reason).ToData(); XrossPeer.TimeAssert(Develop.TIME_ASSERT, "disconnect. とりあえずすべてのconnectionIdに対して、このreserveレイヤに登録があった、みたいなみなしをしてうけいれる。"); if (true) { // remove connectionId from reserved playerId. var succeeded = gameLayer.DiscardConnectionIdOfPlayerId(playerId); if (!succeeded) { XrossPeer.Log("playerId:" + playerId + " のconnectionの廃棄に失敗した。存在しないプレイヤーからの切断っぽい。"); } /* * すでに切断されているので、このプレイヤーへの通信はこの時点で不可能。 */ gameLayer.EnqueOnDisconnect(playerId, data); } }
public void UpdateWorld(int frame, Action <Commands.BaseData, string[]> pub) { foreach (var player in playerContextsInServer) { if (player.auto == null) { continue; } if (!player.isDummy) { if (player.auto.ShouldFalldown(frame)) { continue; } } else { // dummy. if (player.auto.ShouldFalldown(frame)) { if (player.stackedDummyAutos.Count == 0) { continue; } var stackedAutoInfo = player.stackedDummyAutos[0]; player.stackedDummyAutos.RemoveAt(0); // stackedAutoName XrossPeer.Log("next stackedAutoName:" + stackedAutoInfo.autoName); switch (stackedAutoInfo.autoName) { case "DoStalk": { player.dummyTargetId = stackedAutoInfo.parameters[0]; player.dummyMessage = stackedAutoInfo.parameters[1]; player.auto = player.auto.ChangeTo(new DoStalk <PlayerContext, List <PlayerContext> >(frame, player)); break; } default: { XrossPeer.Log("未定義の状態:" + stackedAutoInfo.autoName); break; } } } } player.auto.Update(frame, playerContextsInServer); if (player.stackedCommands.Any()) { var allPlayerIds = playerContextsInServer.Where(p => !p.isDummy).Select(p => p.playerId).ToArray(); foreach (var stackedCommand in player.stackedCommands) { StackPublish(stackedCommand, allPlayerIds); } player.stackedCommands.Clear(); } } }
public const int CONNECTION_ID_LEN = 36; // 65D76DEE-0E68-424E-A18F-6D2CC9656FB3 /** * 受け取ったjobを解釈、contextへと入力する。APIレイヤーはserverContext側にあるので、データを扱うこの辺は変更なしで行けるはず。 */ public void InputDatasToContext(List <byte[]> datas) { // XrossPeer.Log("datas received. datas:" + datas.Count); /* * messageとして受け取ったjobを、list化して読み込む。 */ foreach (var dataArray in datas) { var len = dataArray.Length; if (len < 1 /*state param*/ + CONNECTION_ID_LEN /*connectionId*/) { var invalidMessage = Encoding.ASCII.GetString(dataArray); XrossPeer.Log("illigal format invalidMessage1:" + invalidMessage); continue; } var state = (char)dataArray[0]; // dataArray[2-38] is connectionId, length = definitely CONNECTION_ID_LEN. var connectionId = Encoding.ASCII.GetString(dataArray, 1, CONNECTION_ID_LEN); switch (state) { case STATE_CONNECT: { XrossPeer.Log("STATE_CONNECT"); if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (1 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); context.OnConnected(connectionId, data); } break; } case STATE_STRING_MESSAGE: { // if (1 + CONNECTION_ID_LEN < len) { // var data = Encoding.UTF8.GetString(dataArray, 2 + CONNECTION_ID_LEN, len - (2 + CONNECTION_ID_LEN)); // context.OnMessage(connectionId, data); // } break; } case STATE_BINARY_MESSAGE: { if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (1 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); context.OnMessage(connectionId, data); } break; } case STATE_DISCONNECT_INTENT: { if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); XrossPeer.Log("client closed"); context.OnDisconnected(connectionId, data, "intentional disconnect."); } break; } case STATE_DISCONNECT_ACCIDT: { if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); context.OnDisconnected(connectionId, data, "accidential disconnect."); } break; } case STATE_DISCONNECT_DISQUE_ACKFAILED: { if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (1 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); context.OnDisconnected(connectionId, data, "accidential disconnect."); } break; } case STATE_DISCONNECT_DISQUE_ACCIDT_SENDFAILED: { if (1 + CONNECTION_ID_LEN < len) { var dataLen = len - (1 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (1 + CONNECTION_ID_LEN), data, 0, dataLen); context.OnDisconnected(connectionId, data, "send failed to client. disconnect."); } break; } default: { XrossPeer.Log("undefined websocket state:" + state); break; } } } }
/** * ServerContext Core. * このレイヤがXrossPeerの外側一枚。 */ private void InputToXrossPeer(int frame, string playerId, byte[] data) { /* * validate data. */ var commandAndPlayerId = Commands.ReadCommandAndSourceId(data); var command = commandAndPlayerId.command; var commandPlayerId = commandAndPlayerId.playerId; if (commandPlayerId != playerId) { XrossPeer.Log("実行者と送付者のidがマッチしない、BANする"); return; } /* * extract. * この部分で、 * ・XrossPeer内の対象のAutoが状況変化を受けいれること * ・対象変化を受け入れた場合、そのコマンドを全員に配ること * ・受け入れない場合ここで黙殺すること * を決める。 */ /* * サーバでしか受け取らず、サーバでないと他のユーザーに反射しないシリーズ。 * ユーザーの有無っていう究極的な可否を判定する根っこ。 * * だいたいConnectionServerが吐いてる */ switch (command) { case Commands.CommandEnum.OnConnected: { var onConnected = Commands.FromData <Commands.OnConnected>(data); var onConnectedPlayerId = onConnected.playerId; XrossPeer.Log("connected playerId:" + onConnectedPlayerId); /* * このタイミングで、サーバへのプレイヤーのログインが完了してる。 */ { var pos = new Commands.StructVector3(0, 0, 30); // 適当な位置をでっち上げる var newPlayer = new PlayerContext(onConnectedPlayerId, pos, DirectionEnum.East); world.AddPlayer(newPlayer); StackPublish(new Commands.EntriedId(onConnectedPlayerId, pos, newPlayer.forward), AllConnectedIds()); } /* * ダミーを10人くらい降らせよう。 */ if (AllConnectedIds().Length == 1) { XrossPeer.Log("最初の接続者 onConnectedPlayerId:" + onConnectedPlayerId + " が来たので、ダミーを降らせる。ランダムシードがすげえーー怪しい時があるな。"); var dummyCount = 2; var xRand = new byte[dummyCount]; var zRand = new byte[dummyCount]; var r1 = new System.Random(); r1.NextBytes(xRand); var r2 = new System.Random(); r2.NextBytes(zRand); { var dummyPlayerId = Guid.NewGuid().ToString(); var dir = DirectionEnum.South; var pos = new Commands.StructVector3(10, 10, 30); var dummyPlayer = new PlayerContext(dummyPlayerId, pos, dir); dummyPlayer.isDummy = true; world.AddPlayer(dummyPlayer); } { var dummyPlayerId = Guid.NewGuid().ToString(); var dir = DirectionEnum.South; var pos = new Commands.StructVector3(10, 0, 30); var dummyPlayer = new PlayerContext(dummyPlayerId, pos, dir); dummyPlayer.isDummy = true; world.AddPlayer(dummyPlayer); } } /* * receive world info. */ var playersInfos = world.PlayersInfos(); StackPublish(new Commands.WorldData(onConnectedPlayerId, playersInfos), new string[] { ConnectionIdFromPlayerId(onConnectedPlayerId) }); return; } case Commands.CommandEnum.OnDisconnected: { var onDisconnected = Commands.FromData <Commands.OnDisconnected>(data); var disconnectedPlayerId = onDisconnected.playerId; var reason = onDisconnected.reason; var reasonCode = 0; XrossPeer.Log("disconnected この時点で通信対象リストからは外されている。 disconnectedPlayerId:" + disconnectedPlayerId + " reason:" + reason + " まだなんにもしてない。"); // StackPublish(new Commands.PlayerLeft(disconnectedPlayerId, reasonCode), AllConnectedIds()); return; } } switch (command) { case Commands.CommandEnum.SpawnRequest: { var spawnData = Commands.FromData <Commands.SpawnRequest>(data); var spawnPlayerId = spawnData.playerId; XrossPeer.Log("spawnがきた spawnPlayerId:" + spawnPlayerId); /* * すでにSpawnしてないかとかみて、OKだったらSpawnを返す */ StackPublish(new Commands.Spawn(spawnPlayerId), AllConnectedIds()); return; } // 移動データとかを扱う。 case Commands.CommandEnum.Walk: { var walkData = Commands.FromData <Commands.Walk>(data); var walkingPlayerId = walkData.playerId; var walkingDir = walkData.direction; var walkBasePos = walkData.pos; StackPublish(new Commands.Walk(walkingPlayerId, walkingDir, walkBasePos), AllConnectedIds()); if (!world.IsDummyPlayer(walkingPlayerId)) { var playerInfo = world.GetPlayerInfo(walkingPlayerId); playerInfo.auto = new Walk <PlayerContext, List <PlayerContext> >(gameFrame, playerInfo); } return; } case Commands.CommandEnum.Messaging: { var messageData = Commands.FromData <Commands.Messaging>(data); var senderPlayerId = messageData.playerId; var targetPlayerId = messageData.targetPlayerId; var message = messageData.message; // サーバまで届いているので、そのことの反射をする。 var senderConnectionId = ConnectionIdFromPlayerId(senderPlayerId); StackPublish(new Commands.Messaging(senderPlayerId, senderPlayerId, "我:" + senderPlayerId + ":" + message), new string[] { senderConnectionId }); XrossPeer.Log("message:" + message + " from playerId:" + senderPlayerId + " to targetPlayerId:" + targetPlayerId); /* * is bot, message turns to "order". */ if (world.IsDummyPlayer(targetPlayerId)) { GenerateAnswer(targetPlayerId, senderPlayerId, message, senderConnectionId); return; } var connectionId = ConnectionIdFromPlayerId(targetPlayerId); StackPublish(new Commands.Messaging(senderPlayerId, targetPlayerId, "相手:" + senderPlayerId + ":" + message), new string[] { connectionId }); return; } default: { XrossPeer.Log("gameContextLayer unhandled command:" + command); break; } } }
/** * ServerContextの終了手続き */ public void Teardown() { XrossPeer.TimeAssert(Develop.TIME_ASSERT, "ContextのTeardown処理、なんか必要かな、、"); }
private const int CONNECTION_ID_LEN = 36; // 65D76DEE-0E68-424E-A18F-6D2CC9656FB3 private void ParseData(byte[] dataArray) { var len = dataArray.Length; if (len < 1 /*s or b or c*/ + 1 /*state param*/ + CONNECTION_ID_LEN /*connectionId*/) { var invalidMessage = Encoding.ASCII.GetString(dataArray); XrossPeer.Log("illigal format invalidMessage1:" + invalidMessage); return; } var header = (char)dataArray[0]; switch (header) { case HEADER_CONTROL: case HEADER_STRING: case HEADER_BINARY: { break; } default: { var invalidMessage = Encoding.ASCII.GetString(dataArray); XrossPeer.Log("illigal format invalidMessage2:" + invalidMessage); return; } } var state = (char)dataArray[1]; // dataArray[2-38] is connectionId, length = definitely CONNECTION_ID_LEN. var connectionId = Encoding.ASCII.GetString(dataArray, 2, CONNECTION_ID_LEN); switch (state) { case STATE_CONNECT: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnConnected(connectionId, data); } break; } case STATE_STRING_MESSAGE: { // ignored. break; } case STATE_BINARY_MESSAGE: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnMessage(connectionId, data); } break; } case STATE_DISCONNECT_INTENT: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnDisconnected(connectionId, data, "intentional disconnect."); } break; } case STATE_DISCONNECT_ACCIDT: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnDisconnected(connectionId, data, "accidential disconnect."); } break; } case STATE_DISCONNECT_DISQUE_ACKFAILED: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnDisconnected(connectionId, data, "accidential disconnect."); } break; } case STATE_DISCONNECT_DISQUE_ACCIDT_SENDFAILED: { if (2 + CONNECTION_ID_LEN < len) { var dataLen = len - (2 + CONNECTION_ID_LEN); var data = new byte[dataLen]; Buffer.BlockCopy(dataArray, (2 + CONNECTION_ID_LEN), data, 0, dataLen); OnDisconnected(connectionId, data, "send failed to client. disconnect."); } break; } default: { XrossPeer.Log("undefined websocket state:" + state); break; } } }
public ServerContext(string serverQueueId) { serverContextId = Guid.NewGuid().ToString(); XrossPeer.Log("server generated! serverContextId:" + serverContextId + " serverQueueId:" + serverQueueId); // Updater queueGetJobber = null; // ちょうどDisqueConnectionControllerとDisquuunのあいだをいったりきたりしてるんで齟齬がある。 var disqueId = Guid.NewGuid().ToString(); // disquuun = new Disquuun( // disqueId, // "127.0.0.1", // 7711, // 102400, // connectedConId => { // Action<string, byte[]> Send = (string connectionId, byte[] data) => { // disquuun.AddJob(connectionId, data); // }; // Setup(Send); // XrossPeer.Log("同期的にコンテキストの用意が終わったつもり。 ほんとはいろんな接続があるはず。"); // queueGetJobber = new Updater( // "disquuunGetJobber", // () => { // disquuun.GetJob(new string[]{serverQueueId}, "COUNT", 1000, "NOHANG"); // return true; // } // ); // }, // (command, byteDatas) => { // switch (command) { // case Disquuun.DisqueCommand.INFO: { // var stringData = byteDatas[0]; // var info = Encoding.UTF8.GetString(stringData.bytesArray[0], 0, stringData.bytesArray[0].Length); // XrossPeer.Log("info:" + info); // break; // } // case Disquuun.DisqueCommand.GETJOB: { // var jobIds = new List<string>(); // foreach (var bytes in byteDatas) { // var jobId = Encoding.UTF8.GetString(bytes.bytesArray[0]); // jobIds.Add(jobId); // ParseData(bytes.bytesArray[1]); // } // if (jobIds.Any()) disquuun.FastAck(jobIds.ToArray()); // break; // } // case Disquuun.DisqueCommand.FASTACK: { // // do nothing. // break; // } // default: { // break; // } // } // }, // (failedCommand, reason) => { // XrossPeer.LogError("failedCommand:" + failedCommand + " reason:" + reason); // }, // e => { // XrossPeer.LogError("Disque error:" + e); // if (queueGetJobber != null) queueGetJobber.Quit(); // }, // disconnectedConId => { // XrossPeer.Log("Disque disconnected:" + disqueId); // } // ); }