/// <summary> /// エコーデータの読み込み /// </summary> /// <param name="connection"></param> /// <param name="payload"></param> /// <param name="payload_len"></param> private static void ReadEcho(MrsConnection connection, IntPtr payload, UInt32 payload_len) { mrs.Time read_tm = new mrs.Time(); read_tm.Set(); byte[] read_data = new byte[payload_len]; Marshal.Copy(payload, read_data, 0, (int)payload_len); mrs.Buffer buffer = new mrs.Buffer(); buffer.Write(read_data); while (buffer.GetDataLen() > 0) { mrs.Time write_tm = buffer.ReadTime(); MRS_LOG_DEBUG("ReadEcho data={0} data_len={1} diff_time={2}({3} - {4})", ToString(buffer.GetData()), m_nWriteDataLen, (read_tm - write_tm).ToString(), read_tm.ToString(), write_tm.ToString()); if (!buffer.Read(null, m_nWriteDataLen)) { MRS_LOG_ERR("Lost data. len={0} {1}", buffer.GetDataLen(), mrs.Utility.ToHex(buffer.GetData(), buffer.GetDataLen())); m_bLoop = false; break; } ++m_nReadCount; if (m_nWriteCount * m_nConnections <= m_nReadCount) { MRS_LOG_DEBUG("Since all records have been received, it is finished."); m_bLoop = false; } } }
/// <summary> /// TCPソケット切断時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> private static void OnDisconnect(MrsConnection connection, IntPtr connection_data) { some1disconnected = true; for (int i = 0; i < m_MaxPlayer; i++) { if (connection == m_nowConnect[i]) { MRS_LOG_DEBUG("OnDisconnect {0} : {1} local_mrs_version=0x{2:X} remote_mrs_version=0x{3:X}", ConnectionTypeToString(connection), m_nowConnect[i].ToInt32(), mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY)); m_gameProc.eraseProfileData(i); m_nowConnect[i] = IntPtr.Zero; } if (m_nowConnect[i].ToInt32() == 0) { for (int j = i + 1; j < m_MaxPlayer; j++) { if (m_nowConnect[j].ToInt32() != 0) { m_nowConnect[i] = m_nowConnect[j]; m_nowConnect[j] = IntPtr.Zero; break; } } } //MRS_LOG_DEBUG("now Connect No.{0} : {1}", i, m_nowConnect[i].ToInt32()); } nowplayers = mrs_get_connection_num(); if (nowplayers <= 2) { g_gameon = false; m_gameProc.CloseGame(); } }
/// <summary> /// TCPソケット接続時に呼ばれる /// </summary> /// <param name="connection"></param> private static void OnConnect(MrsConnection connection) { if (g_isPlay || nowplayers >= m_MaxPlayer) { mrs_close(connection); return; } g_isRoom = true; for (int i = 0; i < m_MaxPlayer; i++) { if (m_nowConnect[i].ToInt32() == 0) { m_nowConnect[i] = connection; MRS_LOG_DEBUG("OnConnect {0} : {1} local_mrs_version=0x{2:X} remote_mrs_version=0x{3:X}", ConnectionTypeToString(connection), m_nowConnect[i].ToInt32(), mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY)); nowplayers = mrs_get_connection_num(); MRS_LOG_DEBUG("now Connect No.{0} : {1}", i, m_nowConnect[i].ToInt32()); break; } MRS_LOG_DEBUG("now Connect No.{0} : {1}", i, m_nowConnect[i].ToInt32()); } bool connectAll = true; for (int j = 0; j < m_MaxPlayer; j++) { if (m_nowConnect[j].ToInt32() == 0) { connectAll = false; break; } } if (connectAll) { } mrs_set_cipher(connection, mrs_cipher_create(MrsCipherType.ECDH)); }
/// <summary> /// TCPソケットが新しい接続を生成した時に呼ばれる /// </summary> /// <param name="server"></param> /// <param name="server_data"></param> /// <param name="client"></param> private static void OnNewConnection(MrsServer server, IntPtr server_data, MrsConnection client) { MRS_LOG_DEBUG("OnNewConnection {0} : {1}", ConnectionTypeToString(client), client.ToString()); mrs_set_disconnect_callback(client, m_OnDisconnect); mrs_set_error_callback(client, m_OnError); mrs_set_read_record_callback(client, m_OnReadRecord); OnConnect(client); }
/// <summary> /// エコーデータを書き込み数分書き込む /// </summary> /// <param name="connection"></param> private static void WriteEchoAll(MrsConnection connection) { StringBuilder sb = new StringBuilder(); switch (g_Connect.GetRequest().ConnectionType) { case MrsConnectionType.TCP: sb.Append("TCP "); break; case MrsConnectionType.UDP: sb.Append("UDP "); break; case MrsConnectionType.WS: sb.Append("WS "); break; case MrsConnectionType.WSS: sb.Append("WSS "); break; case MrsConnectionType.TCP_SSL: sb.Append("TCP_SSL"); break; case MrsConnectionType.MRU: sb.Append("MRU"); break; default: sb.Append("INVALID"); break; } if (m_canKeyExchange && m_canEncryptRecords) { sb.Append(" CRYPT"); } else { sb.Append("NOCRYPT"); } byte[] data = new byte[m_nWriteDataLen]; for (uint i = 0; i < m_nWriteCount; ++i) { string str = String.Format("{0} {1}: {2}", sb.ToString(), connection, i + 1); byte[] conv = System.Text.Encoding.UTF8.GetBytes(str); System.Buffer.BlockCopy(conv, 0, data, 0, conv.Length); WriteEcho(connection, data, m_nWriteDataLen); } }
/// <summary> /// レコードのパース /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="seqnum"></param> /// <param name="options"></param> /// <param name="payload_type"></param> /// <param name="_payload"></param> /// <param name="payload_len"></param> private static void ParseRecord(MrsConnection connection, IntPtr connection_data, UInt32 seqnum, UInt16 options, UInt16 payload_type, IntPtr _payload, UInt32 payload_len) { Mrs.MRS_LOG_DEBUG("ParseRecord seqnum=0x{0} options=0x{1:X2} payload={2:X}/{3}", seqnum, options, payload_type, payload_len); // MRS_PAYLOAD_TYPE_BEGIN - MRS_PAYLOAD_TYPE_ENDの範囲内で任意のIDを定義し、対応するアプリケーションコードを記述する switch (payload_type) { case 0x01: ReadEcho(connection, _payload, payload_len); break; default: break; } }
/// <summary> /// コネクションタイプを表す文字列を返す /// </summary> private static string ConnectionTypeToString(MrsConnection connection) { MrsConnectionType type = mrs_connection_get_type(connection); switch (type) { case MrsConnectionType.NONE: { return("NONE"); } case MrsConnectionType.TCP: { return("TCP"); } case MrsConnectionType.UDP: { return("UDP"); } } return("INVALID"); }
/// <summary> /// ソケット接続時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> private static void OnConnect(MrsConnection connection, IntPtr connection_data) { MRS_LOG_DEBUG("OnConnect local_mrs_version=0x{0} remote_mrs_version=0x{1}", mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY)); if (m_canKeyExchange) { mrs_set_cipher(connection, mrs_cipher_create(MrsCipherType.ECDH)); mrs_key_exchange(connection, m_OnKeyExchange); } else { WriteEchoAll(connection); } }
/// <summary> /// エコーデータの書き込み /// </summary> /// <param name="connection"></param> /// <param name="data"></param> private static void WriteEcho(MrsConnection connection, byte[] data, uint data_len) { mrs.Time write_tm = new mrs.Time(); write_tm.Set(); mrs.Buffer buffer = new mrs.Buffer(); buffer.WriteTime(write_tm); buffer.Write(data, data_len); if (m_canValidRecord) { mrs_write_record(connection, m_nRecordOptions, 0x01, buffer.GetData(), buffer.GetDataLen()); } else { mrs_write(connection, buffer.GetData(), buffer.GetDataLen()); } }
/// <summary> /// 実行 /// </summary> public void Run() { try { mrs_initialize(); using (ExitSignal sig = new ExitSignal()) { sig.SetSignal((obj, e) => { m_bLoop = false; Thread.Sleep(10); }); uint connections = 0; for (uint i = 0; i < m_nConnections; ++i) { MrsConnection client = g_Connect.FallbackConnect(); if (client == IntPtr.Zero) { MRS_LOG_ERR("mrs_connect[{0}]: {1}", i, mrs_get_error_string(mrs_get_last_error())); break; } ++connections; } if (connections != m_nConnections) { return; } while (m_bLoop) { mrs_update(); mrs_sleep(m_SleepMsec); } } } catch (Exception e) { MRS_LOG_ERR(e.Message); } finally { mrs_finalize(); } }
/// <summary> /// フォールバック接続時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="request"></param> private static void OnFallbackConnectCallback(MrsConnection connection, mrs.Connect.Request request) { MRS_LOG_DEBUG("OnFallbackConnectCallback connection_type={0} addr={1} port={2} timeout_msec={3}", (int)request.ConnectionType, request.Addr, request.Port, request.TimeoutMsec); mrs_set_connect_callback(connection, m_OnConnect); mrs_set_disconnect_callback(connection, m_OnDisconnect); mrs_set_error_callback(connection, m_OnError); if (m_canValidRecord) { mrs_set_read_record_callback(connection, m_OnReadRecord); } else { mrs_set_read_callback(connection, m_OnRead); } mrs_connection_set_path(connection, m_ConnectionPath); }
/// <summary> /// ソケットにエラーが発生した時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="status"></param> private static void OnError(MrsConnection connection, IntPtr connection_data, MrsConnectionError status) { switch (status) { case MrsConnectionError.CONNECT_ERROR: case MrsConnectionError.CONNECT_TIMEOUT: { MrsConnection client = g_Connect.FallbackConnect(connection); if (client != IntPtr.Zero) { return; } } break; default: break; } MRS_LOG_DEBUG("OnError local_mrs_version=0x{0} remote_mrs_version=0x{1} status={2}", mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY), mrs_get_connection_error_string(status)); }
/// <summary> /// エラー時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="status"></param> private static void OnError(MrsConnection connection, IntPtr connection_data, MrsConnectionError status) { MRS_LOG_DEBUG("OnError {0} local_mrs_version=0x{1:X} remote_mrs_version=0x{2:X} status={3}", ConnectionTypeToString(connection), mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY), mrs_get_connection_error_string(status)); }
/// <summary> /// レコードのパース /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="seqnum"></param> /// <param name="options"></param> /// <param name="payload_type"></param> /// <param name="_payload"></param> /// <param name="payload_len"></param> private static void ParseRecord(MrsConnection connection, IntPtr connection_data, UInt32 seqnum, UInt16 options, UInt16 payload_type, IntPtr _payload, UInt32 payload_len) { //MRS_LOG_DEBUG("ParseRecord seqnum=0x{0:X} options=0x{1:X} payload type={2:X} payload len={3}", seqnum, options, payload_type, payload_len); switch (payload_type) { // MRS_PAYLOAD_TYPE_BEGIN - MRS_PAYLOAD_TYPE_ENDの範囲内で任意のIDを定義し、対応するアプリケーションコードを記述する case 0x01: { g_payloadType = 0x02; int i = 0; for (; i < m_MaxPlayer; i++) { if (connection == m_nowConnect[i]) { for (int j = 0; j < nowplayers; j++) { if (i != j) { IntPtr p_Send = m_gameProc.getProfileData(j); mrs_write_record(m_nowConnect[i], options, g_payloadType, p_Send, (uint)m_gameProc.getProfileSize(j)); MRS_LOG_DEBUG("SENT No.{0} Player to No.{1} Player's Data", i, j); } } break; } } IntPtr p_data = m_gameProc.setProfile(_payload, i); g_payloadType = 0x01; for (int j = 0; j < nowplayers; j++) { if (j == i) { g_payloadType = 0x01; mrs_write_record(m_nowConnect[j], options, g_payloadType, p_data, (uint)Marshal.SizeOf(m_gameProc.getProfile(i))); } if (j != i && m_nowConnect[j] != (IntPtr)0) { g_payloadType = 0x02; mrs_write_record(m_nowConnect[j], options, g_payloadType, p_data, (uint)m_gameProc.getProfileSize(i)); } } } break; // 0x03 ゲームスタート準備 case 0x03: { g_isPlay = true; nowplayers = mrs_get_connection_num(); g_payloadType = 0x03; MRS_LOG_DEBUG("received 0x03 data"); // ゲーム処理初期化 m_gameProc.Initialize(); readybit = 0; g_isRoom = false; // ゲームスタートに必要なデータの作成・送信 IntPtr sendptr = m_gameProc.getStartData((int)nowplayers); for (int i = 0; i < nowplayers; i++) { mrs_write_record(m_nowConnect[i], options, g_payloadType, sendptr, (uint)m_gameProc.getStartDataSize()); } MRS_LOG_DEBUG("sent start data"); Marshal.FreeHGlobal(sendptr); } break; // 0x04 クライアント達の初期位置設定待ち case 0x04: { int i = 0; for (; i < m_MaxPlayer; i++) { if (connection == m_nowConnect[i]) { break; } } MRS_LOG_DEBUG("received 0x04 data from Player no.{0}", i); m_gameProc.setPlayerData(i, _payload); readybit += 1 << i; MRS_LOG_DEBUG("READY BIT : {0}", Convert.ToString(readybit, 2)); int correctBit = 0; for (int j = 0; j < mrs_get_connection_num(); j++) { correctBit += 1 << j; } if (readybit == correctBit) { g_payloadType = 0x04; SendDataEveryone(); MRS_LOG_DEBUG("EVERYONE READY!"); g_gameon = true; } } break; // 0x05 カウントダウン開始の告知 case 0x05: { for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; // 0x06 カウントダウン時間の送受信 case 0x06: { for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; // 0x07 ステージ番号の送受信 case 0x07: unsafe { int stageid = *(int *)_payload; m_gameProc.setStageId(stageid); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; // プレイヤーデータ送受信用PayloadType case 0x12: { if (g_gameon) { int i = 0; for (; i < m_MaxPlayer; i++) { if (connection == m_nowConnect[i]) { break; } } m_gameProc.setPlayerData(i, _payload); for (int j = 0; j < mrs_get_connection_num(); j++) { if (j != i) { if (m_nowConnect[j].ToInt32() != 0) { mrs_write_record(m_nowConnect[j], options, payload_type, _payload, payload_len); } } } } } break; // ショットデータ送受信用Type case 0x13: { if (g_gameon) { IntPtr send = m_gameProc.setShotData(_payload); for (int j = 0; j < nowplayers; j++) { if (m_nowConnect[j].ToInt32() != 0) { mrs_write_record(m_nowConnect[j], options, payload_type, send, payload_len); } } } } break; // 床の落下の合図、全員に合図を送信 case 0x15: { MRS_LOG_DEBUG("RECIEVE 0x15 FALL FLOOR !!"); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; // 弾の反射情報 case 0x16: { MRS_LOG_DEBUG("RECIEVE 0x16 REFLEXION SHOT !!"); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; //------------------------------------ 死亡判定系 0x2# // 落下死 case 0x21: { MRS_LOG_DEBUG("RECIEVE SOMEONE FALLING !!"); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, _payload, payload_len); } } } break; // 被弾死 case 0x22: { MRS_LOG_DEBUG("RECIEVE SOMEONE TAKING SHOT !!"); IntPtr sendHit = m_gameProc.SomeoneDeadHit(_payload); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, payload_type, sendHit, payload_len); } } } break; //------------------------------------ シーン切り替え 0x3# // リザルト表示中 case 0x31: { MRS_LOG_DEBUG("ON_RESULT"); g_gameon = false; m_gameProc.setStageId(1); } break; // ルームに戻る選択を受信 case 0x32: { MRS_LOG_DEBUG("BACK_ROOM"); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, 0x32, null, 0); } } g_isRoom = true; g_isPlay = false; } break; // タイトルに戻る選択を受信 case 0x33: { MRS_LOG_DEBUG("BACK_TITLE"); for (int i = 0; i < nowplayers; i++) { if (m_nowConnect[i].ToInt32() != 0) { mrs_write_record(m_nowConnect[i], options, 0x33, null, 0); } } g_isPlay = false; } break; } }
/// <summary> /// バイナリデータ受信時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="_data"></param> /// <param name="data_len"></param> private static void OnRead(MrsConnection connection, IntPtr connection_data, IntPtr _data, UInt32 data_len) { ReadEcho(connection, _data, data_len); }
/// <summary> /// レコード受信時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> /// <param name="seqnum"></param> /// <param name="options"></param> /// <param name="payload_type"></param> /// <param name="_payload"></param> /// <param name="payload_len"></param> private static void OnReadRecord(MrsConnection connection, IntPtr connection_data, UInt32 seqnum, UInt16 options, UInt16 payload_type, IntPtr _payload, UInt32 payload_len) { ParseRecord(connection, connection_data, seqnum, options, payload_type, _payload, payload_len); }
/// <summary> /// ソケット切断時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> private static void OnDisconnect(MrsConnection connection, IntPtr connection_data) { MRS_LOG_DEBUG("OnDisconnect local_mrs_version=0x{0} remote_mrs_version=0x{1}", mrs_get_version(MRS_VERSION_KEY), mrs_connection_get_remote_version(connection, MRS_VERSION_KEY)); }
/// <summary> /// 鍵交換した時に呼ばれる /// </summary> /// <param name="connection"></param> /// <param name="connection_data"></param> private static void OnKeyExchange(MrsConnection connection, IntPtr connection_data) { MRS_LOG_DEBUG("OnKeyExchange"); WriteEchoAll(connection); }