public static void WriteKeymaps() { byte *table = (byte *)keymapArchive + 4; byte *buf = getBuiltinKeyMapBuffer; for (int x = 0; x < keymapEntries; ++x) { int tSize = 0; int error = 0; int strSize = 0; strSize = BinaryTool.ReadPrefixedString(table, buf, EntryModule.MaxKeyMapNameLength, &error); table += strSize; table += 2; // keymask/statebit // default table tSize = *(int *)table; table += 4; table += tSize; // shifted table tSize = *(int *)table; table += 4; table += tSize; // Write it out. TextMode.WriteLine(buf); } }
private static ISockFilter DecodeCommandFilter(byte[] payload, byte type, byte length, int offset, out ObjectId?serverId) { if (type == 0) { //User ID, not targetting a server ObjectId user = BinaryTool.ReadMongoID(payload, offset); serverId = null; return(new UserSockFilter(user)); } else if (type == 1) { //User ID, targetting server ObjectId user = BinaryTool.ReadMongoID(payload, offset); serverId = BinaryTool.ReadMongoID(payload, offset + 12); return(new UserSockFilter(user)); } else if (type == 2) { //Server ID, all tribes serverId = BinaryTool.ReadMongoID(payload, offset); return(new ServerSockFilter(serverId.Value)); } else if (type == 3) { //Server ID, specific tribe serverId = BinaryTool.ReadMongoID(payload, offset); int tribeId = BinaryTool.ReadInt32(payload, offset + 12); return(new ServerTribeSockFilter(serverId.Value, tribeId)); } else { throw new Exception("Unknown filter type " + type + "!"); } }
/// <summary> /// Gets the address of a builtin key map, by it's numeric ID. Good /// for iterating through the list of builtin key maps. /// </summary> public static void *GetBuiltinKeyMap(int id) { byte *table = (byte *)keymapArchive + 4; byte *buf = stackalloc byte [EntryModule.MaxKeyMapNameLength]; int error = 0; for (int x = 0; x < keymapEntries; ++x) { if (x == id) { return((void *)table); } // name-size (x), name string (x), keymask and statebit (2) table += 2 + BinaryTool.ReadPrefixedString(table, buf, EntryModule.MaxKeyMapNameLength, &error); // table size (4), default table (x) table += 4 + *(int *)table; // table size (4), shifted table (x) table += 4 + *(int *)table; } return(null); }
public override async Task OnReceiveBinary(byte[] data, int length) { //If we're not authenticated, handle that first. Log("RECEIVE", "GOT MESSAGE of length " + length); if (!authenticated) { await HandleAuthentication(data, length); return; } //Read the opcode and index ushort opcode = BinaryTool.ReadUInt16(data, 0); ulong index = BinaryTool.ReadUInt64(data, 2); //Switch on this opcode Log("RECEIVE", "Got message with opcode " + opcode + " / index " + index); if (opcode == 1) { await HandleRPCMessage(data, length, index); } else { await WriteResponse(index, 404); } }
public byte[] OnDeployCommand(CoreNetworkServer server, CoreNetworkOpcode opcode, byte[] payload) { //Get the parameters CoreNetworkServerType serverType = (CoreNetworkServerType)BinaryTool.ReadInt16(payload, 0); string configString = Encoding.UTF8.GetString(payload, 4, payload.Length - 4); byte count = payload[2]; //Validate that we support this if (!Program.server_types.ContainsKey(serverType)) { return(CreateFailResponse("This server manager is not configured to support this type of server.")); } //Get the config data JObject serverConfig; try { serverConfig = JsonConvert.DeserializeObject <JObject>(configString); } catch { return(CreateFailResponse("Config JSON is invalid.")); } //Create servers List <ManagerInstance> instances = new List <ManagerInstance>(); for (int i = 0; i < count; i++) { ManagerInstance instance; try { instance = Program.server_types[serverType].CreateInstance(i == count - 1, serverConfig); } catch (Exception ex) { return(CreateFailResponse($"Failed to create instance: {ex.Message} {ex.StackTrace}")); } //Start instance instance.StartProcess(); instances.Add(instance); } //Validate instances is running Thread.Sleep(500); foreach (var i in instances) { if (!i.IsProcessRunning()) { return(CreateFailResponse($"A spawned instance ({i.settings.server_id}) ended early. More may have failed.")); } } //Create response payload byte[] response = new byte[3]; response[0] = 0x00; BinaryTool.WriteUInt16(response, 1, (ushort)instances[0].settings.server_id); return(response); }
public byte[] OnDestroyCommand(CoreNetworkServer server, CoreNetworkOpcode opcode, byte[] payload) { //Get requested ID ushort id = BinaryTool.ReadUInt16(payload, 0); //Find requested server ID ManagerInstance instance = null; foreach (var s in Program.server_types) { foreach (var p in s.Value.instances) { if (p.settings.server_id == id) { instance = p; } } } //Check if failed if (instance == null) { return(CreateFailResponse("Could not find requested instance.")); } //End instance.RemoveInstance().GetAwaiter().GetResult(); //Return OK status return(new byte[] { 0x00 }); }
public void GrayIntSwitchTest() { Assert.AreEqual(1, BinaryTool.GrayToInt(BinaryTool.IntToGray(1, 3))); Assert.AreEqual(0, BinaryTool.GrayToInt(BinaryTool.IntToGray(0, 1))); Assert.AreEqual(0, BinaryTool.GrayToInt(BinaryTool.IntToGray(0, 10))); Assert.AreEqual(17, BinaryTool.GrayToInt(BinaryTool.IntToGray(17, 5))); Assert.AreEqual(255, BinaryTool.GrayToInt(BinaryTool.IntToGray(255, 8))); Assert.AreEqual(156, BinaryTool.GrayToInt(BinaryTool.IntToGray(156, 20))); }
/// <summary> /// Writes a standard response: the only thing this will ever respond with /// </summary> /// <param name="index"></param> /// <param name="opcode"></param> /// <returns></returns> private async Task WriteResponse(ulong index, ushort opcode) { //This includes just 10 bytes: // 8 bytes: The index of the request // 2 bytes: The response opcode. 0 means that all is good //Create payload byte[] payload = new byte[10]; BinaryTool.WriteUInt64(payload, 0, index); BinaryTool.WriteUInt16(payload, 8, opcode); //Send await SendData(payload); }
public byte[] OnUpdateAllCommand(CoreNetworkServer server, CoreNetworkOpcode opcode, byte[] payload) { //Read token uint token = BinaryTool.ReadUInt32(payload, 0); var eventSender = new OperationProgressClient(this, token); //Begin thread var t = new Thread(() => { PackageUpdater.BeginUpdate(eventSender); }); t.IsBackground = true; t.Start(); return(new byte[0]); }
private static void OnIncomingRPCCommand_PrivledgedMessage(byte[] payload) { //Read header RPCOpcode opcode = (RPCOpcode)BitConverter.ToInt32(payload, 2); ObjectId serverId = BinaryTool.ReadMongoID(payload, 6); int payloadCount = BitConverter.ToInt32(payload, 18); //Clone list List <RPCConnection> clientsSafe = new List <RPCConnection>(); lock (connections) clientsSafe.AddRange(connections); //Begin reading array int offset = 22; for (int i = 0; i < payloadCount; i++) { //Read header data int tribeId = BitConverter.ToInt32(payload, offset + 0); int payloadLength = BitConverter.ToInt32(payload, offset + 4); offset += 8; //Decode payload JObject data = JsonConvert.DeserializeObject <JObject>(Encoding.UTF8.GetString(payload, offset, payloadLength)); //Create filter and command ISockFilter filter = new ServerTribeSockFilter(serverId, tribeId); var cmd = new SendRPCMessageCommand(opcode.ToString(), serverId, data); //Search to send messages foreach (var c in clientsSafe) { //Filter if (!filter.CheckFilter(c)) { continue; } //Send c.EnqueueMessage(cmd); } } }
private void HandleLoginCommand(MasterControlClient session, RouterMessage msg) { //Get details int loginId = BitConverter.ToInt16(msg.payload, 0); byte[] loginKey = new byte[16]; Array.Copy(msg.payload, 2, loginKey, 0, 16); //Search for a server DeltaManagerServer server = null; lock (Program.servers) { foreach (var s in Program.servers) { if (s.id == loginId) { //Authenticate key if (BinaryTool.CompareBytes(s.key, loginKey)) { server = s; } } } } //Check if it was successful if (server != null) { //Set server.SetTransport(session); session.authenticated = true; //Log logger.Log("HandleLoginCommand", $"Client {session.GetDebugName()} successfully logged in as server ID {server.id}.", DeltaLogLevel.Low); } else { //Failed logger.Log("HandleLoginCommand", $"Client {session.GetDebugName()} attempted login, but failed.", DeltaLogLevel.Low); } }
public static void ListKeyMaps() { int count = KeyMap.GetBuiltinKeyMapsCount(); byte *str = stackalloc byte [EntryModule.MaxKeyMapNameLength]; int error = 0; byte *keymap = null; int len = 0; TextMode.Write(count); TextMode.WriteLine(" available keymaps:"); for (int x = 0; x < count; ++x) { keymap = (byte *)KeyMap.GetBuiltinKeyMap(x); len = BinaryTool.ReadPrefixedString(keymap, str, EntryModule.MaxKeyMapNameLength, &error); TextMode.Write(" "); TextMode.WriteLine(str); } }
// Start is called before the first frame update void Start() { _testDataObj = new TestSaveData(); Debug.Log("实例化一个数据类对象,用来存放具体数据"); uiInputTxt.onValueChanged.AddListener(GetInputTxt); uiBtnSave.onClick.AddListener( () => { BinaryTool.SaveBinaryData <TestSaveData>(_testDataObj, "DataFile"); Debug.Log("序列化对象, _testDataObj, 内容为:" + _testDataObj.inputStr); }); uiBtnShow.onClick.AddListener( () => { _testDataObj = BinaryTool.ReadBinaryData <TestSaveData>("DataFile"); Debug.Log("反序列化对象, _testDataObj, 读取内容为:" + _testDataObj.inputStr); uiTxtOutput.text = _testDataObj.inputStr; }); }
/// <summary> /// Reads the data encoded by the CreateSteamIdTokenString function /// </summary> /// <param name="encoded"></param> public SteamIdToken ReadSteamIdTokenString(string encoded) { //Read as bytes byte[] data; try { data = Convert.FromBase64String(encoded); } catch { return(null); } //Validate if (data.Length < 16 + 1) { return(null); } //Read the hash byte[] hash = new byte[16]; Array.Copy(data, 0, hash, 0, 16); //Get the HMAC of this steamTokenKey.CopyTo(data, 0); byte[] challengeHash = new HMACMD5(steamTokenKey).ComputeHash(data); //Validate if (!BinaryTool.CompareBytes(hash, challengeHash)) { return(null); } //Data OK! Read it now return(new SteamIdToken { version = data[16], steam_id = Encoding.ASCII.GetString(data, 17, data.Length - 17) }); }
/// <summary> /// Handles the first binary message we get. It will always be for authentication. /// </summary> /// <param name="data"></param> /// <param name="length"></param> /// <returns></returns> private async Task HandleAuthentication(byte[] data, int length) { //This message follows the following format: // 256 bytes: Authentication key // 8 bytes: Program identification name // 4 bytes: System version //Verify if (length != 256 + 8 + 4) { Log("AUTHENTICATION", "Authentication length mismatch!"); await DisconnectAsync(WebSocketCloseStatus.InvalidPayloadData, "DELTA_RPC_DISCONNECT_INVALID_AUTHENTICATION_PAYLOAD"); return; } //Extract info byte[] key = BinaryTool.CopyFromArray(data, 0, 256); program_identity = Encoding.ASCII.GetString(data, 256, 8); version = BinaryTool.ReadInt32(data, 264); //Validate the key if (!BinaryTool.CompareBytes(Program.master_key, key)) { authenticated = false; Log("AUTHENTICATION", "Authentication failed!"); await DisconnectAsync(WebSocketCloseStatus.PolicyViolation, "DELTA_RPC_DISCONNECT_INVALID_AUTHENTICATION_KEY"); return; } //We're now ready for authentication! authenticated = true; Log("AUTHENTICATION", "Authentication OK!"); await WriteResponse(0, 0); }
static void *GetBuiltinKeyMap(byte *name, int nameLen) { #if VERBOSE_KeyMap_INIT TextMode.Write("Key Map Name: "); TextMode.Write(name); TextMode.WriteLine(); TextMode.Write("Key Map Name Length: "); TextMode.Write(nameLen); TextMode.WriteLine(); #endif byte *table = (byte *)keymapArchive + 4; byte *ret_table; byte *buf = getBuiltinKeyMapBuffer; Diagnostics.Assert(nameLen > 0, "KeyMap.GetBuiltinKeyMap(): key map name is too small"); Diagnostics.Assert(nameLen <= EntryModule.MaxKeyMapNameLength, "KeyMap.GetBuiltinKeyMap(): key map name is too large"); for (int x = 0; x < keymapEntries; ++x) { int nSize = 0; int tSize = 0; int error = 0; int strSize = 0; strSize = BinaryTool.ReadPrefixedString(table, buf, EntryModule.MaxKeyMapNameLength, &error); table += strSize; nSize = ByteString.Length(buf); #if VERBOSE_KeyMap_INIT TextMode.Write("nsize: "); TextMode.Write(nSize); TextMode.WriteLine(); TextMode.Write("found keymap: "); TextMode.WriteLine(buf); #endif ret_table = table; table += 2; // keymask/statebit // default table tSize = *(int *)table; #if VERBOSE_KeyMap_INIT TextMode.Write("Default-table size:"); TextMode.Write(tSize); TextMode.WriteLine(""); #endif table += 4; table += tSize; // shifted table tSize = *(int *)table; #if VERBOSE_KeyMap_INIT TextMode.Write("Shifted-table size:"); TextMode.Write(tSize); TextMode.WriteLine(""); #endif table += 4; table += tSize; if (nSize == nameLen && ByteString.Compare(name, buf, nameLen) == 0) { return(ret_table); } } return(null); }
public async Task WriteBinaryResponse(List <DbStructure> structures) { //Binary mode writes out structures in binary structs //HEADER FORMAT: // 4 static Signature, says "DWMS" // 4 int32 File type version, should be 3 // 4 int32 Metadata version // 4 int32 Structure count // 4 int32 Saved epoch // 4 int32 RESERVED // 4 int32 RESERVED // 4 int32 RESERVED //TOTAL: 32 bytes //Struct format: // 2 short Metadata Index // 1 byte Rotation (in degrees, 0-360, but scaled so that 256=360) // 1 byte Flags (SEE BELOW) // 4 float Position X // 4 float Position Y // 4 int32 ID // 4 float Position Z // 4 int32 Tribe ID //TOTAL: 24 bytes each //FLAGS //0: Has inventory //1: Is remove //2: RESERVED //3: RESERVED //4: RESERVED //5: RESERVED //6: RESERVED //7: RESERVED //Set headers e.Response.ContentLength = (24 * structures.Count) + 32; e.Response.ContentType = "application/octet-stream"; e.Response.StatusCode = 200; //Create and send file header byte[] buf = new byte[32]; buf[0] = 0x44; buf[1] = 0x57; buf[2] = 0x4D; buf[3] = 0x53; BinaryTool.WriteInt32(buf, 4, 3); //Version tag BinaryTool.WriteInt32(buf, 8, 0); BinaryTool.WriteInt32(buf, 12, structures.Count); BinaryTool.WriteInt32(buf, 16, 0); //Was epoch, now unused BinaryTool.WriteInt32(buf, 20, 0); BinaryTool.WriteInt32(buf, 24, 0); BinaryTool.WriteInt32(buf, 28, 0); await e.Response.Body.WriteAsync(buf, 0, 32); //Loop through structures and find where they are foreach (var t in structures) { //Get data StructureMetadata metadata = Program.structureMetadata.Where(x => x.names.Contains(t.classname)).FirstOrDefault(); int index = Program.structureMetadata.IndexOf(metadata); //Produce flags byte flags = 0; if (t.has_inventory) { flags |= 0x01 << 0; } //Write parts BinaryTool.WriteInt16(buf, 0, (short)index); buf[2] = (byte)(t.location.yaw * 0.70833333333333333333333333333333f); //Scales this to fit the 0-360 degrees into 0-255 buf[3] = flags; BinaryTool.WriteFloat(buf, 4, t.location.x); BinaryTool.WriteFloat(buf, 8, t.location.y); BinaryTool.WriteInt32(buf, 12, t.structure_id); BinaryTool.WriteFloat(buf, 16, t.location.z); BinaryTool.WriteInt32(buf, 20, t.tribe_id); //Write to stream await e.Response.Body.WriteAsync(buf, 0, 24); } }
public void BinaryToIntTest() { Assert.AreEqual(BinaryTool.BinaryToInt(new int[] { 1, 1 }), 3); Assert.AreEqual(BinaryTool.BinaryToInt(new int[] { 1, 1, 0, 0, 0, 0, 0, 0 }), 3); Assert.AreEqual(BinaryTool.BinaryToInt(new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }), 255); }
public void IntToBinaryTest() { Assert.AreEqual(BinaryTool.IntToBinary(3, 2), new int[] { 1, 1 }); Assert.AreEqual(BinaryTool.IntToBinary(3, 8), new int[] { 1, 1, 0, 0, 0, 0, 0, 0 }); Assert.AreEqual(BinaryTool.IntToBinary(255, 8), new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }); }
public async Task WriteBinaryResponse(List <DbInventory> inventories) { //Binary mode writes out structures in binary structs //In format header, name table, inventory struct //HEADER FORMAT: // 4 static Signature, says "DWMI" // 4 int32 File type version, should be 1 // 4 int32 RESERVED // 4 int32 Inventory count // 4 int32 Saved epoch // 4 int32 Name table entry count // 4 int32 RESERVED // 4 int32 RESERVED //TOTAL: 32 bytes //The name table comes next, and is just a list of classnames, with a byte indicating their length before //Inventory Struct format: // 8 int64 Holder ID // 1 byte Holder type // 2 int16 Item count // 4 int32 Tribe ID // ? Item[] Items //TOTAL: VARIABLE //Item struct format: // 8 int64 Item ID // 4 int32 Name table classname index // 4 float Durability // 2 int16 Stack size // 2 int16 Flags // 1 byte Custom data count // ? CD[] Custom datas, number specified above //Total: VARIABLE //CD (Custom Data) format: // 2 int16 Tag // 2 int16 String length // ? string Data //Set headers e.Response.ContentType = "application/octet-stream"; e.Response.StatusCode = 200; //Create name table List <string> names = new List <string>(); foreach (var i in inventories) { foreach (var ii in i.items) { if (!names.Contains(ii.classname)) { names.Add(ii.classname); } } } //Create and send file header byte[] buf = new byte[32768]; buf[0] = 0x44; buf[1] = 0x57; buf[2] = 0x4D; buf[3] = 0x49; BinaryTool.WriteInt32(buf, 4, 2); //Version tag BinaryTool.WriteInt32(buf, 8, 0); BinaryTool.WriteInt32(buf, 12, inventories.Count); BinaryTool.WriteInt32(buf, 16, 0); //Was epoch, now unused BinaryTool.WriteInt32(buf, 20, names.Count); BinaryTool.WriteInt32(buf, 24, 0); BinaryTool.WriteInt32(buf, 28, 0); await e.Response.Body.WriteAsync(buf, 0, 32); //Send name table foreach (var name in names) { //Write to buffer byte[] d = Encoding.UTF8.GetBytes(name); buf[0] = (byte)d.Length; if (d.Length > byte.MaxValue) { throw new Exception("Encoding failed, name is too long!"); } Array.Copy(d, 0, buf, 1, d.Length); //Send on network await e.Response.Body.WriteAsync(buf, 0, d.Length + 1); } //Send inventories foreach (var inventory in inventories) { //Create header BinaryTool.WriteUInt64(buf, 0, inventory.holder_id); buf[8] = (byte)inventory.holder_type; BinaryTool.WriteInt16(buf, 9, (short)inventory.items.Length); BinaryTool.WriteInt32(buf, 11, inventory.tribe_id); int offset = 15; //Add items foreach (var i in inventory.items) { //Create header BinaryTool.WriteUInt64(buf, offset, i.item_id); BinaryTool.WriteInt32(buf, offset + 8, names.IndexOf(i.classname)); BinaryTool.WriteFloat(buf, offset + 12, i.durability); BinaryTool.WriteInt16(buf, offset + 16, (short)i.stack_size); BinaryTool.WriteInt16(buf, offset + 18, (short)i.flags); buf[offset + 20] = (byte)i.custom_data.Count; offset += 21; //Write custom datas foreach (var c in i.custom_data) { //Write key byte[] d = Encoding.UTF8.GetBytes(c.Key); BinaryTool.WriteInt16(buf, offset, (short)d.Length); Array.Copy(d, 0, buf, offset + 2, d.Length); offset += 2 + d.Length; //Write value d = Encoding.UTF8.GetBytes(c.Value); BinaryTool.WriteInt16(buf, offset, (short)d.Length); Array.Copy(d, 0, buf, offset + 2, d.Length); offset += 2 + d.Length; } } //Send data await e.Response.Body.WriteAsync(buf, 0, offset); offset = 0; } }
private void button1_Click(object sender, EventArgs e) { BinaryTool bTool = new BinaryTool(); List <string> outputs = new List <string>(); string[] sentences = SanitizeInputSentences(); foreach (string sentence in sentences) { string[] keywords = sentence.Split(' '); /* * [0] Decimal <Input Type> * [1] 103 <Value> * [2] to <Redundant> * [3] Binary <Output Type> */ switch (keywords[0]) { case "decimal": { if (keywords[3] == "binary") { outputs.Add(bTool.DecimalToBinary(int.Parse(keywords[1]))); } } break; case "binary": { if (keywords[3] == "decimal") { outputs.Add(bTool.BinaryToDecimal(keywords[1]).ToString()); } } break; case "octal": { if (keywords[3] == "binary") { outputs.Add(bTool.OctalToBinary(int.Parse(keywords[1]))); } } break; case "hexadecimal": { if (keywords[3] == "decimal") { outputs.Add(bTool.HexToDecimal(keywords[1]).ToString()); } else if (keywords[3] == "binary") { outputs.Add(bTool.HexToBinary(keywords[1]).ToString()); } } break; } } txtOutput.Clear(); int i = 0; foreach (string output in outputs) { i++; string finalOutput = output; // Remove leading zeroes if (chkTrimZeroes.Checked) { finalOutput = output.TrimStart('0'); } txtOutput.AppendText(string.Format("{0}{1}" + ((i != outputs.Count) ? "\r\n\r\n" : ""), (chkIndexedOutput.Checked) ? i.ToString() + " : " : "", finalOutput)); } Console.WriteLine("Buffer Size: " + sentences.Length); }
/// <summary> /// Handles an RPC message: opcode 1. This will fire events to clients by a filter /// </summary> /// <param name="data"></param> /// <param name="length"></param> /// <returns></returns> private async Task HandleRPCMessage(byte[] data, int length, ulong index) { //This follows the following format, starting at byte 10 // 4 bytes: int - Payload length "x" // x bytes: string - Payload // 2 bytes: ushort - Filter type // [Dependant on filter type] //Read the length of the payload and read the payload int payloadLength = BinaryTool.ReadInt32(data, 10); byte[] payload = BinaryTool.CopyFromArray(data, 14, payloadLength); Log("RPC-MESSAGE", "Payload length " + payloadLength); //Read the filter code ushort filterCode = BinaryTool.ReadUInt16(data, 14 + payloadLength); Log("RPC-MESSAGE", "Filter type " + filterCode); //Pack contents PackedWebSocketMessage packed = new PackedWebSocketMessage(payload, payloadLength, WebSocketMessageType.Text); //Read the filter and send messages Task pending; if (filterCode == 0) { //USER_ID - Read the user ID // 12 bytes: MongoDB ID - User ID ObjectId userId = BinaryTool.ReadMongoID(data, 14 + payloadLength + 2); pending = RPCEventDispatcher.SendDataToUserById(RPCType.RPCSession, userId, packed); } else if (filterCode == 1) { //SERVER - Sends to all members of a server // 12 bytes: MongoDB ID - Server ID ObjectId serverId = BinaryTool.ReadMongoID(data, 14 + payloadLength + 2); pending = RPCEventDispatcher.SendDataToServerById(RPCType.RPCSession, serverId, packed); } else if (filterCode == 2) { //SERVER_TRIBE - Sends to a tribe of a server // 12 bytes: MongoDB ID - Server ID // 4 bytes: Int - ARK Tribe ID ObjectId serverId = BinaryTool.ReadMongoID(data, 14 + payloadLength + 2); int tribeId = BinaryTool.ReadInt32(data, 14 + payloadLength + 2 + 12); pending = RPCEventDispatcher.SendDataToServerTribeById(RPCType.RPCSession, serverId, tribeId, packed); } else { await WriteResponse(index, 400); return; } //Wait for sending to complete Log("RPC-MESSAGE", "Sending message....pending..."); await pending; Log("RPC-MESSAGE", "Message sent OK!"); //Send OK await WriteResponse(index, 0); }