public override void Handle(ByteReader data) { object target = null; if (targetType != null) { target = SyncSerialization.ReadSyncObject(data, targetType); if (target == null) { return; } } object value = SyncSerialization.ReadSyncObject(data, fieldType); if (cancelIfValueNull && value == null) { return; } object index = null; if (indexType != null) { index = SyncSerialization.ReadSyncObject(data, indexType); } preApply?.Invoke(target, value); MpLog.Debug($"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"); }
static void Main(string[] args) { var _log = new MpLog(Common.LogFileName, "MAIN"); if (args.Length > 0) { var fn = args[0]; var proc = Process.GetCurrentProcess(); var pname = proc.ProcessName; var procs = Process.GetProcessesByName(pname); _log.Write($"num-procs:{procs.Length} pid:{proc.Id} arg-fn:{fn}"); // Ensure only one playing at a time. if (procs.Length == 1) { _log.Write($"================================== {DateTime.Now} ============================================="); _log.Write($"main thread enter"); // I'm the first, start normally by passing the file name. Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Transport(fn)); _log.Write($"main thread exit"); } else { // If this is the second instance, alert the primary by connecting and sending the new file name. _log.Write($"sub thread enter"); Client client = new(Common.PipeName, Common.LogFileName); var res = client.Send(fn, 1000); switch (res) { case ClientStatus.Error: _log.Write($"Client error:{client.Error}", true); MessageBox.Show(client.Error, "Error!"); break; case ClientStatus.Timeout: _log.Write($"Client timeout", true); MessageBox.Show("Timeout!"); break; } _log.Write($"sub thread exit {res}"); } } else { MessageBox.Show("Missing file name argument"); } }
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 SupplyCrossRefs() { if (sharedCrossRefs == null) { return; } if (!loading) { Log.Warning("Tried to supply cross refs without calling ScribeUtil.StartLoading()"); return; } defaultCrossRefs ??= Scribe.loader.crossRefs.loadedObjectDirectory; Scribe.loader.crossRefs.loadedObjectDirectory = sharedCrossRefs; MpLog.Debug($"Cross ref supply: {sharedCrossRefs.allObjectsByLoadID.Count} {sharedCrossRefs.allObjectsByLoadID.LastOrDefault()} {Faction.OfPlayer}"); }
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); } }
public static object ReadSyncObject(ByteReader data, SyncType syncType) { MpContext context = data.MpContext(); Map map = context.map; Type type = syncType.type; try { if (typeof(object) == type) { return(null); } if (type.IsByRef) { return(null); } if (syncWorkersEarly.TryGetValue(type, out SyncWorkerEntry syncWorkerEntryEarly)) { object res = null; if (syncWorkerEntryEarly.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntryEarly.Invoke(new ReadingSyncWorker(data), ref res); return(res); } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } byte[] exposableData = data.ReadPrefixedBytes(); return(ReadExposable.MakeGenericMethod(type).Invoke(null, new[] { exposableData, null })); } if (typeof(ISynchronizable).IsAssignableFrom(type)) { var obj = Activator.CreateInstance(type); ((ISynchronizable)obj).Sync(new ReadingSyncWorker(data)); return(obj); } if (type.IsEnum) { Type enumType = Enum.GetUnderlyingType(type); return(ReadSyncObject(data, enumType)); } if (type.IsArray && type.GetArrayRank() == 1) { Type elementType = type.GetElementType(); ushort length = data.ReadUShort(); Array arr = Array.CreateInstance(elementType, length); for (int i = 0; i < length; i++) { arr.SetValue(ReadSyncObject(data, elementType), i); } return(arr); } if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == typeof(List <>)) { ListType specialList = ReadSync <ListType>(data); if (specialList == ListType.MapAllThings) { return(map.listerThings.AllThings); } if (specialList == ListType.MapAllDesignations) { return(map.designationManager.allDesignations); } Type listType = type.GetGenericArguments()[0]; ushort size = data.ReadUShort(); IList list = (IList)Activator.CreateInstance(type, size); for (int j = 0; j < size; j++) { list.Add(ReadSyncObject(data, listType)); } return(list); } if (type.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { Type element = type.GetGenericArguments()[0]; return(ReadSyncObject(data, typeof(List <>).MakeGenericType(element))); } if (type.GetGenericTypeDefinition() == typeof(Nullable <>)) { bool isNull = data.ReadBool(); if (isNull) { return(null); } bool hasValue = data.ReadBool(); if (!hasValue) { return(Activator.CreateInstance(type)); } Type nullableType = type.GetGenericArguments()[0]; return(Activator.CreateInstance(type, ReadSyncObject(data, nullableType))); } } // Def is a special case until the workers can read their own type if (typeof(Def).IsAssignableFrom(type)) { ushort shortHash = data.ReadUShort(); if (shortHash == 0) { return(null); } Def def = (Def)GetDefByIdMethod.MakeGenericMethod(type).Invoke(null, new object[] { shortHash }); if (def == null) { throw new Exception($"Couldn't find {type} with short hash {shortHash}"); } return(def); } // Where the magic happens if (syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { object res = null; if (syncWorkerEntry.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntry.Invoke(new ReadingSyncWorker(data), ref res); return(res); } throw new SerializationException("No reader for type " + type); } catch (Exception e) { MpLog.Error($"Error reading type: {type}, {e}"); throw; } }
public static void WriteSyncObject(ByteWriter data, object obj, SyncType syncType) { MpContext context = data.MpContext(); Type type = syncType.type; LoggingByteWriter log = data as LoggingByteWriter; log?.LogEnter(type.FullName + ": " + (obj ?? "null")); if (obj != null && !type.IsAssignableFrom(obj.GetType())) { throw new SerializationException($"Serializing with type {type} but got object of type {obj.GetType()}"); } try { if (typeof(object) == type) { return; } if (type.IsByRef) { return; } if (syncWorkersEarly.TryGetValue(type, out var syncWorkerEntryEarly)) { syncWorkerEntryEarly.Invoke(new WritingSyncWorker(data), ref obj); return; } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } IExposable exposable = obj as IExposable; data.WritePrefixedBytes(ScribeUtil.WriteExposable(exposable)); return; } if (typeof(ISynchronizable).IsAssignableFrom(type)) { ((ISynchronizable)obj).Sync(new WritingSyncWorker(data)); return; } if (type.IsEnum) { Type enumType = Enum.GetUnderlyingType(type); WriteSyncObject(data, Convert.ChangeType(obj, enumType), enumType); return; } if (type.IsArray && type.GetArrayRank() == 1) { Type elementType = type.GetElementType(); Array arr = (Array)obj; if (arr.Length > ushort.MaxValue) { throw new Exception($"Tried to serialize a {elementType}[] with too many ({arr.Length}) items."); } data.WriteUShort((ushort)arr.Length); foreach (object e in arr) { WriteSyncObject(data, e, elementType); } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List <>)) { ListType specialList = ListType.Normal; Type listType = type.GetGenericArguments()[0]; if (listType == typeof(Thing) && obj == Find.CurrentMap.listerThings.AllThings) { context.map = Find.CurrentMap; specialList = ListType.MapAllThings; } else if (listType == typeof(Designation) && obj == Find.CurrentMap.designationManager.allDesignations) { context.map = Find.CurrentMap; specialList = ListType.MapAllDesignations; } WriteSync(data, specialList); if (specialList == ListType.Normal) { IList list = (IList)obj; if (list.Count > ushort.MaxValue) { throw new Exception($"Tried to serialize a {type} with too many ({list.Count}) items."); } data.WriteUShort((ushort)list.Count); foreach (object e in list) { WriteSyncObject(data, e, listType); } } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>)) { bool isNull = obj == null; data.WriteBool(isNull); if (isNull) { return; } bool hasValue = (bool)obj.GetPropertyOrField("HasValue"); data.WriteBool(hasValue); Type nullableType = type.GetGenericArguments()[0]; if (hasValue) { WriteSyncObject(data, obj.GetPropertyOrField("Value"), nullableType); } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { IEnumerable e = (IEnumerable)obj; Type elementType = type.GetGenericArguments()[0]; var listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (var o in e) { if (list.Count > ushort.MaxValue) { throw new Exception($"Tried to serialize a {type} with too many ({list.Count}) items."); } list.Add(o); } WriteSyncObject(data, list, listType); return; } // special case if (typeof(Def).IsAssignableFrom(type)) { Def def = obj as Def; data.WriteUShort(def != null ? def.shortHash : (ushort)0); return; } // Where the magic happens if (syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { syncWorkerEntry.Invoke(new WritingSyncWorker(data), ref obj); return; } log?.LogNode("No writer for " + type); throw new SerializationException("No writer for type " + type); } catch (Exception e) { MpLog.Error($"Error writing type: {type}, obj: {obj}, {e}"); throw; } finally { log?.LogExit(); } }
/// <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; } } } }
public override void Handle(ByteReader data) { object target; if (targetTransformer != null) { target = targetTransformer.Reader.DynamicInvoke(SyncSerialization.ReadSyncObject(data, targetTransformer.NetworkType)); } else { target = ReadTarget(data); } if (targetType != null && target == null) { MpLog.Debug($"SyncMethod {this} read target null"); return; } if (!instancePath.NullOrEmpty()) { target = target.GetPropertyOrField(instancePath); } var args = new object[argTypes.Length]; for (int i = 0; i < argTypes.Length; i++) { if (argTransformers[i] == null) { args[i] = SyncSerialization.ReadSyncObject(data, argTypes[i]); } } for (int i = 0; i < argTypes.Length; i++) { if (argTransformers[i] is { } trans) { args[i] = trans.Reader.DynamicInvoke(SyncSerialization.ReadSyncObject(data, trans.NetworkType)); } } 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.Debug($"Invoked {method} on {target} with {args.Length} params {args.ToStringSafeEnumerable()}"); method.Invoke(target, args); afterCall?.Invoke(target, args); }
public void OnNetworkError(IPEndPoint endPoint, SocketError error) { MpLog.Error($"Net client error {error}"); }