public override void Handle(ByteReader data) { object target = null; if (targetType != null) { target = Sync.ReadSyncObject(data, targetType); if (target == null) { return; } } object value = Sync.ReadSyncObject(data, fieldType); if (cancelIfValueNull && value == null) { return; } object index = null; if (indexType != null) { index = Sync.ReadSyncObject(data, indexType); } preApply?.Invoke(target, value); MpLog.Log($"Set {memberPath} in {target} to {value}, map {data.MpContext().map}, index {index}"); MpReflection.SetValue(target, memberPath, value, index); postApply?.Invoke(target, value); }
public static void TryConnect(IPAddress address, int port) { EventBasedNetListener listener = new EventBasedNetListener(); Multiplayer.session = new MultiplayerSession(); NetManager netClient = new NetManager(listener); netClient.Start(); netClient.ReconnectDelay = 300; netClient.MaxConnectAttempts = 8; listener.PeerConnectedEvent += peer => { IConnection conn = new MpNetConnection(peer); conn.username = Multiplayer.username; conn.State = ConnectionStateEnum.ClientJoining; Multiplayer.session.client = conn; MpLog.Log("Net client connected"); }; listener.PeerDisconnectedEvent += (peer, info) => { string reason; if (info.AdditionalData.AvailableBytes > 0) { reason = info.AdditionalData.GetString(); } else { reason = DisconnectReasonString(info.Reason); if (info.SocketErrorCode != SocketError.Success) { reason += ": " + info.SocketErrorCode; } } Multiplayer.session.disconnectNetReason = reason; ConnectionStatusListeners.TryNotifyAll_Disconnected(); OnMainThread.StopMultiplayer(); MpLog.Log("Net client disconnected"); }; listener.NetworkReceiveEvent += (peer, reader, method) => { byte[] data = reader.GetRemainingBytes(); Multiplayer.HandleReceive(new ByteReader(data), method == DeliveryMethod.ReliableOrdered); }; listener.NetworkErrorEvent += (endpoint, error) => { Log.Warning($"Net client error {error}"); }; Multiplayer.session.netClient = netClient; netClient.Connect(address.ToString(), port, ""); }
public override void Handle(ByteReader data) { object target = Activator.CreateInstance(delegateType); for (int i = 0; i < fieldPaths.Length; i++) { string path = fieldPaths[i]; string noTypePath = MpReflection.RemoveType(path); Type fieldType = fieldTypes[i]; object value; if (fieldType.IsCompilerGenerated()) { value = Activator.CreateInstance(fieldType); } else { value = Sync.ReadSyncObject(data, fieldType); } if (value == null) { if (cancelIfAnyNullBlacklist != null && !cancelIfAnyNullBlacklist.Contains(noTypePath)) { return; } if (path.EndsWith("$this")) { return; } if (cancelIfNull != null && cancelIfNull.Contains(noTypePath)) { return; } } if (removeNullsFromLists != null && removeNullsFromLists.Contains(noTypePath) && value is IList list) { list.RemoveNulls(); } MpReflection.SetValue(target, path, value); } if (context.HasFlag(SyncContext.MapSelected) && cancelIfNoSelectedObjects && Find.Selector.selected.Count == 0) { return; } object[] parameters = Sync.ReadSyncObjects(data, argTypes); MpLog.Log("Invoked delegate method " + method + " " + delegateType); method.Invoke(target, parameters); }
public void OnPeerConnected(NetPeer peer) { IConnection conn = new MpNetConnection(peer); conn.username = Multiplayer.username; conn.State = ConnectionStateEnum.ClientJoining; Multiplayer.session.client = conn; MpLog.Log("Net client connected"); }
public void Add(SyncInfo info) { if (Multiplayer.session.desynced) { return; } if (TickPatch.Skipping) { return; } if (buffer.Count == 0) { buffer.Add(info); return; } if (buffer[0].local == info.local) { buffer.Add(info); if (buffer.Count > 30) { buffer.RemoveAt(0); } } else { while (buffer.Count > 0 && buffer[0].startTick < info.startTick) { buffer.RemoveAt(0); } if (buffer.Count == 0) { buffer.Add(info); } else if (buffer.First().startTick == info.startTick) { var first = buffer.RemoveFirst(); var error = first.Compare(info); if (error != null) { MpLog.Log($"Desynced {lastValidTick}: {error}"); Multiplayer.session.desynced = true; OnMainThread.Enqueue(() => OnDesynced(first, info, error)); } else { lastValidTick = first.startTick; lastValidArbiter = Multiplayer.session.ArbiterPlaying; } } } }
public void OnPeerDisconnected(NetPeer peer, DisconnectInfo info) { var reader = new ByteReader(info.AdditionalData.GetRemainingBytes()); Multiplayer.session.HandleDisconnectReason((MpDisconnectReason)reader.ReadByte(), reader.ReadPrefixedBytes()); ConnectionStatusListeners.TryNotifyAll_Disconnected(); OnMainThread.StopMultiplayer(); MpLog.Log("Net client disconnected"); }
public void OnPeerConnected(NetPeer peer) { ConnectionBase conn = new LiteNetConnection(peer); conn.username = Multiplayer.username; conn.State = ConnectionStateEnum.ClientJoining; conn.StateObj.StartState(); Multiplayer.session.client = conn; Multiplayer.session.ReapplyPrefs(); MpLog.Log("Net client connected"); }
public override void Handle(ByteReader data) { object target = null; if (targetType != null) { target = Sync.ReadSyncObject(data, targetType); if (target == null) { return; } } if (!instancePath.NullOrEmpty()) { target = target.GetPropertyOrField(instancePath); } object[] args = null; if (argTypes != null) { args = Sync.ReadSyncObjects(data, argTypes); if (cancelIfAnyArgNull && args.Any(a => a == null)) { return; } } if (context.HasFlag(SyncContext.MapSelected) && cancelIfNoSelectedMapObjects && Find.Selector.selected.Count == 0) { return; } if (context.HasFlag(SyncContext.WorldSelected) && cancelIfNoSelectedWorldObjects && Find.WorldSelector.selected.Count == 0) { return; } beforeCall?.Invoke(target, args); MpLog.Log("Invoked " + method + " on " + target + " with " + args.Length + " params " + args.ToStringSafeEnumerable()); method.Invoke(target, args); afterCall?.Invoke(target, args); }
public static void ScheduleCommand(ScheduledCommand cmd) { MpLog.Log($"Cmd: {cmd.type}, faction: {cmd.factionId}, map: {cmd.mapId}, ticks: {cmd.ticks}"); cachedMapCmds.GetOrAddNew(cmd.mapId).Add(cmd); if (Current.ProgramState != ProgramState.Playing) { return; } if (cmd.mapId == ScheduledCommand.Global) { Multiplayer.WorldComp.cmds.Enqueue(cmd); } else { cmd.GetMap()?.AsyncTime().cmds.Enqueue(cmd); } }
/// <summary> /// Adds a client opinion to the <see cref="knownClientOpinions"/> list and checks that it matches the most recent currently in there. If not, a desync event is fired. /// </summary> /// <param name="newOpinion">The <see cref="ClientSyncOpinion"/> to add and check.</param> public void AddClientOpinionAndCheckDesync(ClientSyncOpinion newOpinion) { //If we've already desynced, don't even bother if (Multiplayer.session.desynced) { return; } //If we're skipping ticks, again, don't bother if (TickPatch.Skipping) { return; } //If this is the first client opinion we have nothing to compare it with, so just add it if (knownClientOpinions.Count == 0) { knownClientOpinions.Add(newOpinion); return; } if (knownClientOpinions[0].isLocalClientsOpinion == newOpinion.isLocalClientsOpinion) { knownClientOpinions.Add(newOpinion); if (knownClientOpinions.Count > 30) { knownClientOpinions.RemoveAt(0); } } else { //Remove all opinions that started before this one, as it's the most up to date one while (knownClientOpinions.Count > 0 && knownClientOpinions[0].startTick < newOpinion.startTick) { knownClientOpinions.RemoveAt(0); } //If there are none left, we don't need to compare this new one if (knownClientOpinions.Count == 0) { knownClientOpinions.Add(newOpinion); } else if (knownClientOpinions.First().startTick == newOpinion.startTick) { //If these two contain the same tick range - i.e. they start at the same time, cause they should continue to the current tick, then do a comparison. var oldOpinion = knownClientOpinions.RemoveFirst(); //Actually do the comparison to find any desync var desyncMessage = oldOpinion.CheckForDesync(newOpinion); if (desyncMessage != null) { MpLog.Log($"Desynced after tick {lastValidTick}: {desyncMessage}"); Multiplayer.session.desynced = true; OnMainThread.Enqueue(() => HandleDesync(oldOpinion, newOpinion, desyncMessage)); } else { //Update fields lastValidTick = oldOpinion.startTick; arbiterWasPlayingOnLastValidTick = Multiplayer.session.ArbiterPlaying; } } } }