public void TestVector2() { Vector2[] inputs = { Vector2.right, Vector2.up, Vector2.zero, Vector2.one, Vector2.positiveInfinity, new Vector2(0.1f, 3.1f) }; foreach (Vector2 input in inputs) { NetworkWriter writer = new NetworkWriter(); writer.WriteVector2(input); NetworkReader reader = new NetworkReader(writer.ToArray()); Vector2 output = reader.ReadVector2(); Assert.That(output, Is.EqualTo(input)); } }
public void TestVector4() { Vector4[] inputs = { Vector3.right, Vector3.up, Vector4.zero, Vector4.one, Vector4.positiveInfinity, new Vector4(0.1f, 3.1f, 1.4f, 4.9f) }; foreach (Vector4 input in inputs) { var writer = new NetworkWriter(); writer.WriteVector4(input); var reader = new NetworkReader(writer.ToArray()); Vector4 output = reader.ReadVector4(); Assert.That(output, Is.EqualTo(input)); } }
public void TestSetLengthZeroes() { NetworkWriter writer = new NetworkWriter(); writer.WriteString("I saw"); writer.WriteInt64(0xA_FADED_DEAD_EEL); writer.WriteString("and ate it"); int position = writer.Position; writer.SetLength(10); // Setting length should set position too Assert.That(writer.Position, Is.EqualTo(10)); // lets grow it back and check there's zeroes now. writer.SetLength(position); byte[] data = writer.ToArray(); for (int i = position; i < data.Length; i++) { Assert.That(data[i], Is.EqualTo(0), $"index {i} should have value 0"); } }
public void AddPlayerMessageTest() { // try setting value with constructor AddPlayerMessage message = new AddPlayerMessage { value = new byte[] { 1, 2 } }; // serialize NetworkWriter writer = new NetworkWriter(); message.Serialize(writer); byte[] writerData = writer.ToArray(); // deserialize the same data - do we get the same result? AddPlayerMessage fresh = new AddPlayerMessage(); fresh.Deserialize(new NetworkReader(writerData)); Assert.That(fresh.value, Is.EqualTo(message.value)); }
public void Roundtrip() { NetworkWriter w = new NetworkWriter(); w.Write(new ChildMessage { parentValue = 3, childValue = 4 }); byte[] arr = w.ToArray(); NetworkReader r = new NetworkReader(arr); ChildMessage received = new ChildMessage(); received.Deserialize(r); Assert.AreEqual(3, received.parentValue); Assert.AreEqual(4, received.childValue); }
public void TestColor() { Color[] inputs = { Color.black, Color.blue, Color.cyan, Color.yellow, Color.magenta, Color.white, new Color(0.401f,0.2f,1.0f,0.123f) }; foreach (Color input in inputs) { NetworkWriter writer = new NetworkWriter(); writer.WriteColor(input); NetworkReader reader = new NetworkReader(writer.ToArray()); Color output = reader.ReadColor(); Assert.That(output, Is.EqualTo(input)); } }
public void ObjectHideMessageTest() { // try setting value with constructor ObjectHideMessage message = new ObjectHideMessage { netId = 42, }; // serialize NetworkWriter writer = new NetworkWriter(); message.Serialize(writer); byte[] writerData = writer.ToArray(); // deserialize the same data - do we get the same result? ObjectHideMessage fresh = new ObjectHideMessage(); fresh.Deserialize(new NetworkReader(writerData)); Assert.That(fresh.netId, Is.EqualTo(message.netId)); }
public void TestVector2Int() { Vector2Int[] inputs = { Vector2Int.down, Vector2Int.up, Vector2Int.left, Vector2Int.zero, new Vector2Int(-1023,-999999), new Vector2Int(257,12345), new Vector2Int(0x7fffffff,-12345), }; foreach (Vector2Int input in inputs) { NetworkWriter writer = new NetworkWriter(); writer.WriteVector2Int(input); NetworkReader reader = new NetworkReader(writer.ToArray()); Vector2Int output = reader.ReadVector2Int(); Assert.That(output, Is.EqualTo(input)); } }
public void TestVector3() { Vector3[] inputs = { Vector3.right, Vector3.up, Vector3.zero, Vector3.one, Vector3.positiveInfinity, Vector3.forward, new Vector3(0.1f,3.1f,1.4f) }; foreach (Vector3 input in inputs) { NetworkWriter writer = new NetworkWriter(); writer.WriteVector3(input); NetworkReader reader = new NetworkReader(writer.ToArray()); Vector3 output = reader.ReadVector3(); Assert.That(output, Is.EqualTo(input)); } }
//Write the position and rotation to the data structure public virtual void Tick(ref RecordData data) { if (writer == null) { writer = new NetworkWriter(); } else #if ENABLE_MIRROR { writer.Position = 0; } #else { writer.SeekZero(); } #endif writer.Write(transform.position); writer.Write(transform.rotation); #if ENABLE_MIRROR data.bytes = writer.ToArray(); #else data.bytes = writer.AsArray(); #endif }
public CharData_Msg Serialize() { NetworkWriter write = new NetworkWriter(); write.Write(index); write.Write(players.Count); for (int p = 0; p < players.Count; p++) { write.Write(players[p].nick); write.Write(players[p].sessionID); write.Write(players[p].sceneID); write.Write(players[p].HPMax); } CharData_Msg data = new CharData_Msg(); data.array = write.ToArray(); return(data); }
// Send client position data every so often to those connected IEnumerator SendPositionCoroutine() { NetworkWriter nr = new NetworkWriter(); while (true) { yield return(new WaitForSeconds(0.1f)); // Anything to do? if (clientList.Count == 0) { continue; } // Reset stream nr.SeekZero(); nr.Write((byte)PacketTypeEnum.Position); foreach (var item in clientList) { nr.Write(item.obj.GetInstanceID()); nr.Write(item.obj.transform.position); // Don't pack too much // Fix me! Send more than one packet instead if (nr.Position > 1300) { break; } } // Send data out byte[] buffer = nr.ToArray(); byte error; //Debug.Log(string.Format("Sending data size {0}", buffer.Length)); foreach (var item in clientList) { NetworkTransport.Send(m_hostId, item.connectionId, reliableChannel, buffer, buffer.Length, out error); } } }
public void TestColor32() { Color32[] inputs = new Color32[] { Color.black, Color.blue, Color.cyan, Color.yellow, Color.magenta, Color.white, new Color32(0xab, 0xcd, 0xef, 0x12), new Color32(125, 126, 0, 255) }; foreach (Color32 input in inputs) { NetworkWriter writer = new NetworkWriter(); writer.Write(input); NetworkReader reader = new NetworkReader(writer.ToArray()); Color32 output = reader.ReadColor32(); Assert.That(output, Is.EqualTo(input)); } }
public void TestOverwritingData() { NetworkWriter writer = new NetworkWriter(); writer.WriteMatrix4x4(Matrix4x4.identity); writer.WriteDecimal(1.23456789m); writer.Position += 10; writer.WriteVector3(Vector3.negativeInfinity); writer.Position = 46; // write right at the boundary before SetLength writer.WriteInt64(0xfeed_babe_c0ffee); // test that SetLength clears data beyond length writer.SetLength(50); // check that jumping leaves 0s between writer.Position = 100; writer.WriteString("no worries, m8"); writer.Position = 64; writer.WriteBoolean(true); // check that clipping off the end affect ToArray()'s length writer.SetLength(128); byte[] output = writer.ToArray(); //Debug.Log(BitConverter.ToString(output)); byte[] expected = { 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xEE, 0xFF, 0xC0, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x6E, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x72, 0x69, 0x65, 0x73, 0x2C, 0x20, 0x6D, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; Assert.That(output, Is.EqualTo(expected)); }
public void SendMessage(string targetObjectName, string methodName, object param, object param2, object param3, object param4, object param5, object param6, object param7, MsgType msgType = MsgType.Reliable) { if (debugRPC) { LogUI("CAVE2 SendMessage (Param 9)'" + methodName + "' on " + targetObjectName); } NetworkWriter writer = new NetworkWriter(); writer.StartMessage(207); writer.Write(targetObjectName); writer.Write(methodName); writer.Write(7); ParamToByte(writer, param); ParamToByte(writer, param2); ParamToByte(writer, param3); ParamToByte(writer, param4); ParamToByte(writer, param5); ParamToByte(writer, param6); ParamToByte(writer, param7); writer.FinishMessage(); ServerSendMsgToClients(writer.ToArray(), msgType); #if USING_GETREAL3D if (getReal3D.Cluster.isMaster) { getReal3D.RpcManager.call("SendCAVE2RPC9", targetObjectName, methodName, param, param2, param3, param4, param5, param6, param7); } else { SendCAVE2RPC9(targetObjectName, methodName, param, param2, param3, param4, param5, param6, param7); } #else SendCAVE2RPC9(targetObjectName, methodName, param, param2, param3, param4, param5, param6, param7); #endif }
public void TestPackedUInt32() { NetworkWriter writer = new NetworkWriter(); writer.WritePackedUInt32(0); writer.WritePackedUInt32(234); writer.WritePackedUInt32(2284); writer.WritePackedUInt32(67821); writer.WritePackedUInt32(16777210); writer.WritePackedUInt32(16777219); writer.WritePackedUInt32(UInt32.MaxValue); NetworkReader reader = new NetworkReader(writer.ToArray()); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(0)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(234)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(2284)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(67821)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(16777210)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(16777219)); Assert.That(reader.ReadPackedUInt32(), Is.EqualTo(UInt32.MaxValue)); }
public void NetworkPongMessageTest() { // try setting value with constructor NetworkPongMessage message = new NetworkPongMessage { clientTime = DateTime.Now.ToOADate(), serverTime = DateTime.Now.ToOADate(), }; // serialize NetworkWriter writer = new NetworkWriter(); message.Serialize(writer); byte[] writerData = writer.ToArray(); // deserialize the same data - do we get the same result? NetworkPongMessage fresh = new NetworkPongMessage(); fresh.Deserialize(new NetworkReader(writerData)); Assert.That(fresh.clientTime, Is.EqualTo(message.clientTime)); Assert.That(fresh.serverTime, Is.EqualTo(message.serverTime)); }
/// <summary> /// Serialize input data. /// </summary> /// <returns></returns> public byte[] Serialize() { if (m_InputData == null || m_InputData.Count <= 0) { return(null); } NetworkWriter writer = new NetworkWriter(); writer.Write((short)m_InputData.Count); for (int i = 0; i < m_InputData.Count; i++) { InputData inputData = m_InputData[i]; writer.WriteBytesAndSize(inputData.GetData()); } m_InputData.Clear(); return(writer.ToArray()); }
public void OnDeserializeSafelyShouldDetectAndHandleDeSerializationMismatch() { // create a networkidentity with our test components GameObject gameObject = new GameObject(); NetworkIdentity identity = gameObject.AddComponent <NetworkIdentity>(); SerializeTest1NetworkBehaviour comp1 = gameObject.AddComponent <SerializeTest1NetworkBehaviour>(); SerializeMismatchNetworkBehaviour compMiss = gameObject.AddComponent <SerializeMismatchNetworkBehaviour>(); SerializeTest2NetworkBehaviour comp2 = gameObject.AddComponent <SerializeTest2NetworkBehaviour>(); // set some unique values to serialize comp1.value = 12345; comp2.value = "67890"; // serialize NetworkWriter ownerWriter = new NetworkWriter(); NetworkWriter observersWriter = new NetworkWriter(); identity.OnSerializeAllSafely(true, ownerWriter, out int ownerWritten, observersWriter, out int observersWritten); // reset component values comp1.value = 0; comp2.value = null; // deserialize all NetworkReader reader = new NetworkReader(ownerWriter.ToArray()); LogAssert.ignoreFailingMessages = true; // warning log because of serialization mismatch identity.OnDeserializeAllSafely(reader, true); LogAssert.ignoreFailingMessages = false; // the mismatch component will fail, but the one before and after // should still work fine. that's the whole point. Assert.That(comp1.value, Is.EqualTo(12345)); Assert.That(comp2.value, Is.EqualTo("67890")); // clean up GameObject.DestroyImmediate(gameObject); }
public void SerializeAreAddedWhenEmptyInStruct() { NetworkWriter writer = new NetworkWriter(); const int someValue = 3; writer.Write(new SomeStructMessage { someValue = someValue, }); byte[] arr = writer.ToArray(); NetworkReader reader = new NetworkReader(arr); SomeStructMessage received = reader.Read <SomeStructMessage>(); Assert.AreEqual(someValue, received.someValue); int writeLength = writer.Length; int readLength = reader.Position; Assert.That(writeLength == readLength, $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}"); }
void Update() { CheckForDirtyFsmVariables(); if (!isServer && hasAuthority && observed != null && synchDirection == SyncDirection.ClientToServer) { CurrentStatus = Status.IsWriting; if (Time.realtimeSinceStartup - _lastClientToServerSynch > m_SendInterval) { _lastClientToServerSynch = Time.realtimeSinceStartup; Debug.Log("Synching from Client to Server"); _nw = new NetworkWriter(); PlayMakerUnetUtils.WriteStreamFromFsmVars(this, _nw, observed.Fsm, NetworkSynchVariableLOT, true); this.Cmd_SerializeData(_nw.ToArray()); } } else if (isServer && hasAuthority) { } }
public void SendsVauesInParentAndChildClass() { var writer = new NetworkWriter(); writer.Write(new ChildMessage { parentValue = 3, childValue = 4 }); byte[] arr = writer.ToArray(); var reader = new NetworkReader(arr); ChildMessage received = reader.Read <ChildMessage>(); Assert.AreEqual(3, received.parentValue); Assert.AreEqual(4, received.childValue); int writeLength = writer.Length; int readLength = reader.Position; Assert.That(writeLength == readLength, $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}"); }
public void TestSynchronizingObjects() { // set up a "server" object var player1 = CreateBehaviour <MockPlayer>(); var myGuild = new MockPlayer.Guild { name = "Back street boys" }; player1.guild = myGuild; // serialize all the data as we would for the network player1.Identity.OnSerializeAll(true, ownerWriter, observersWriter); // set up a "client" object var player2 = CreateBehaviour <MockPlayer>(); // apply all the data from the server object reader.Reset(ownerWriter.ToArray()); player2.Identity.OnDeserializeAll(reader, true); // check that the syncvars got updated Assert.That(player2.guild.name, Is.EqualTo("Back street boys"), "Data should be synchronized"); }
public static void Send(int hostId, int connectionId, int channelId, MessageBase message, short messageType) { NetworkWriter writer = new NetworkWriter(); writer.StartMessage(messageType); message.Serialize(writer); writer.FinishMessage(); byte[] writerData = writer.ToArray(); int bufferLength = P2PController.bufferLength; NetworkTransport.Send(hostId, connectionId, channelId, writerData, P2PController.bufferLength, out P2PController.error); P2PController.CheckError("Send"); if (Recorder.session != null) { Recorder.session.messagesSent++; Recorder.session.AddLeavingBandwidth(bufferLength); if (channelId == P2PChannels.ReliableChannelId) { Recorder.session.importantMessagesSent++; } } }
public void WriteUShortAfterReset() { ushort value1 = 0b0101; ushort value2 = 0x1000; // write first value writer.WriteUInt16(value1); reader.Reset(writer.ToArray()); ushort out1 = reader.ReadUInt16(); Assert.That(out1, Is.EqualTo(value1)); // reset and write 2nd value writer.Reset(); writer.WriteUInt16(value2); reader.Reset(writer.ToArray()); ushort out2 = reader.ReadUInt16(); Assert.That(out2, Is.EqualTo(value2), "Value 2 was incorrect"); }
public void TestResetSetsPotionAndLength() { writer.WriteString("I saw"); writer.WriteInt64(0xA_FADED_DEAD_EEL); writer.WriteString("and ate it"); writer.Reset(); Assert.That(writer.BitPosition, Is.EqualTo(0)); Assert.That(writer.ByteLength, Is.EqualTo(0)); byte[] data = writer.ToArray(); Assert.That(data, Is.Empty); }
/// <summary> /// Using in thread /// </summary> /// <param name="obj"></param> static void CreateRoomsList(object obj) { Room[] rooms = (Room[])obj; int w = 0; NetworkWriter wr = new NetworkWriter(); NetworkWriter constructor = new NetworkWriter(); byte[] buffer; for (int i = 0; i < rooms.Length; i++) { if (w == 10) { wr.StartMessage(Networking_msgType_Sr.RoomList); wr.Write(w); buffer = constructor.ToArray(); foreach (byte b in buffer) { wr.Write(b); } wr.FinishMessage(); result.Add(wr); w = 0; } if (w == 0) { wr = new NetworkWriter(); constructor = new NetworkWriter(); } if (rooms[i] != null) { if (rooms[i].maxPlayerNumber > rooms[i].playerNumber) { Room room = rooms[i]; constructor.Write(i); constructor.Write(room._name); constructor.Write(room.mapID); constructor.Write((short)room.playerNumber); constructor.Write((short)room.maxPlayerNumber); if (room.password) { constructor.Write(true); } else { constructor.Write(false); } w++; } } if (i + 1 == rooms.Length) { wr.StartMessage(Networking_msgType_Sr.RoomList); wr.Write(w); buffer = constructor.ToArray(); foreach (byte b in buffer) { wr.Write(b); } wr.FinishMessage(); result.Add(wr); } } }
byte[] MsgToByte(MessageBase msg) { _writer.SeekZero(); _writer.Write(msg); return(_writer.ToArray()); }
public void MakeNextBatch_OneMessage() { // add message byte[] message = { 0x01, 0x02 }; batcher.AddMessage(new ArraySegment <byte>(message)); // make batch bool result = batcher.MakeNextBatch(writer); Assert.That(result, Is.EqualTo(true)); Assert.That(writer.ToArray().SequenceEqual(message)); }
void Update() { // detect move mode bool hasPath = false; if (agent != null) { hasPath = HasPath(); } // server should detect teleports / react if we got too far off // do this BEFORE isLocalPlayer actions so that agent.ResetPath can be // detected properly? otherwise localplayer wasdmovement cmd may // overwrite it if (isServer) { // neither click or wasd movement, but position changed further than 'speed'? // then we must have teleported, no other way to move this fast. if (entity.GetType() == typeof(Player)) { // just use OnSerialize via SetDirtyBit only sync when position // changed. set dirty bits 0 or 1 SetDirtyBit((uint)(HasMovedOrRotated() ? 1 : 0)); // find out if another component modifed transform.position since // last time. we check it before modiying the position again. // -> this is necessary because the server might reset the position // after respawning, but the client actually has authority over // the position. we still need to be able to force it somewhere. if (!isLocalPlayer) { //Debug.Log(name + " warpcheckDistance=" + Vector3.Distance(lastInternalServerPosition, transform.position) + " / " + rubberbandDistance); // != check is not good enough because precision might be lost // over the network, especially when compressing. so check the // distance if (Vector3.Distance(lastInternalServerPosition, transform.position) > rubberbandDistance) { // force position on clients RpcForcePosition(transform.position); // reset interpolation points on server too so we don't // interpolate from pre-teleport position to new one start = null; goal = null; } } } else { if (!hasPath && agent.velocity == Vector3.zero && Vector3.Distance(transform.position, lastServerPosition) > agent.speed) { // set NetworkNavMeshAgent dirty so that onserialize is // triggered and the client receives the position change SetDirtyBit(1); //Debug.Log(name + " teleported!"); } // different destination than the one that we received from the // client? then set dirty so it gets synced to others // (epsilon comparison needed for float precision over the network) if (HasPath() && Vector3.Distance(agent.destination, lastReceivedDestination) > epsilon) { // set dirty so onserialize notifies others SetDirtyBit(1); //Debug.Log(name + " destination changed"); // reset last received destination so we don't detect it again // until it changes again lastReceivedDestination = agent.destination; } // detect agent.Reset: // - had a path before but not anymore? // - and we never reached the planned destination even though path was canceled? // (epsilon comparison needed for float precision over the network) if (hadPath && !hasPath && Vector3.Distance(transform.position, lastReceivedDestination) > epsilon) { // set dirty so onserialize notifies others SetDirtyBit(1); //Debug.Log(name + " path was reset"); // reset last received destination so we don't detect it again // until it changes again lastReceivedDestination = agent.destination; // send target rpc to the local player so he doesn't ignore it // -> better than a 'bool forceReset' flag for OnSerialize // because new Cmds might come in before OnSerialize was // called, which could lead to race conditions // -> not in host mode (!isLocalPlayer) if (!isLocalPlayer) { TargetResetMovement(connectionToClient); } } lastServerPosition = transform.position; hadPath = hasPath; } } // local player can move freely // (do nothing if in host mode though. don't want to overwrite the // player's destination with Cmds here.) if (isClient) { if (entity.GetType() == typeof(Player)) { // always send to server if we and arent the server // (like NetworkTransform localAuthority mode) // -> only if connectionToServer has been initialized yet too if (!isServer && isLocalPlayer && connectionToServer != null) { // check only each 'sendinterval', otherwise we send at whatever // the player's tick rate is, which is like DDOS if (Time.time - lastClientSendTime >= GetNetworkSendInterval()) { if (HasMovedOrRotated()) { // send message to server NetworkWriter writer = new NetworkWriter(); SerializeIntoWriter(writer, true, transform.position, transform.rotation, compressRotation, syncRotationX, syncRotationY, syncRotationZ); CmdClientToServerSync(writer.ToArray()); } lastClientSendTime = Time.time; } } // apply interpolation on client for all players // except for local player if he has authority and handles it himself if (!isLocalPlayer) { // received one yet? (initialized?) if (goal != null) { // teleport or interpolate if (NeedsTeleport()) { SetPositionAndRotation(goal.position, goal.rotation); } else { SetPositionAndRotation(InterpolatePosition(start, goal, transform.position), InterpolateRotation(start, goal, transform.rotation)); } } } } else { if (isLocalPlayer && !isServer) { // click movement and destination changed since last sync? // then send the latest DESTINATION if (hasPath && agent.destination != lastSentDestination) { // don't send all the time to not DDOS the server // note: if we check time BEFORE detecting move then moves are // detected very late and cause additional latency and // might cause move->idle->move effect if second move // doesn't come in fast enough, etc. if (NetworkTime.time > lastSentTime + GetNetworkSendInterval()) { //CmdMovedClick(agent.destination, agent.stoppingDistance); lastSentDestination = agent.destination; lastSentTime = NetworkTime.time; } } // wasd movement and velocity changed since last sync? // then send the latest POSITION (not velocity) // why is velocity not zero after stopping? else if (!hasPath && agent.velocity != Vector3.zero && transform.position != lastSentPosition) { // don't send all the time to not DDOS the server // note: if we check time BEFORE detecting move then moves are // detected very late and cause additional latency and // might cause move->idle->move effect if second move // doesn't come in fast enough, etc. if (NetworkTime.time > lastSentTime + GetNetworkSendInterval()) { CmdMovedWASD(transform.position, transform.rotation); lastSentPosition = transform.position; lastSentTime = NetworkTime.time; } } } } } }