Пример #1
0
        public static void SendCurrentGameData(bool async)
        {
            var mapsData = new Dictionary <int, byte[]>(OnMainThread.cachedMapData);
            var gameData = OnMainThread.cachedGameData;

            void Send()
            {
                var writer = new ByteWriter();

                writer.WriteInt32(mapsData.Count);
                foreach (var mapData in mapsData)
                {
                    writer.WriteInt32(mapData.Key);
                    writer.WritePrefixedBytes(GZipStream.CompressBuffer(mapData.Value));
                }

                writer.WritePrefixedBytes(GZipStream.CompressBuffer(gameData));

                byte[] data = writer.ToArray();

                OnMainThread.Enqueue(() => Multiplayer.Client.SendFragmented(Packets.Client_AutosavedData, data));
            };

            if (async)
            {
                ThreadPool.QueueUserWorkItem(c => Send());
            }
            else
            {
                Send();
            }
        }
Пример #2
0
        public void HandleCommand(ByteReader data)
        {
            ScheduledCommand cmd = ScheduledCommand.Deserialize(data);

            cmd.issuedBySelf = data.ReadBool();
            OnMainThread.ScheduleCommand(cmd);
        }
Пример #3
0
        private static void TickSync()
        {
            foreach (SyncField f in Sync.bufferedFields)
            {
                if (!f.inGameLoop)
                {
                    continue;
                }

                Sync.bufferedChanges[f].RemoveAll((k, data) =>
                {
                    if (OnMainThread.CheckShouldRemove(f, k, data))
                    {
                        return(true);
                    }

                    if (!data.sent && TickPatch.Timer - data.timestamp > 30)
                    {
                        f.DoSync(k.first, data.toSend, k.second);
                        data.sent      = true;
                        data.timestamp = TickPatch.Timer;
                    }

                    return(false);
                });
            }
        }
Пример #4
0
        private static void Rehost()
        {
            LongEventHandler.QueueLongEvent(() =>
            {
                Find.GameInfo.permadeathMode = false;
                // todo handle the other faction def too
                Multiplayer.DummyFaction.def = FactionDefOf.Ancients;

                OnMainThread.StopMultiplayer();

                var doc = SaveLoad.SaveGame();
                MemoryUtility.ClearAllMapsAndWorld();

                Current.Game                     = new Game();
                Current.Game.InitData            = new GameInitData();
                Current.Game.InitData.gameToLoad = "play";

                LoadPatch.gameToLoad = doc;

                LongEventHandler.ExecuteWhenFinished(() =>
                {
                    HostWindow window = new HostWindow(null, true);
                    window.forcePause = true;
                    window.absorbInputAroundWindow = true;
                    Find.WindowStack.Add(window);
                });
            }, "Play", "MpConverting", true, null);
        }
Пример #5
0
        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 PostClose()
 {
     base.PostClose();
     if (shouldCloseConnection)
     {
         OnMainThread.StopMultiplayer();
     }
 }
Пример #7
0
        public override void PostClose()
        {
            OnMainThread.StopMultiplayer();

            if (returnToServerBrowser)
            {
                Find.WindowStack.Add(new ServerBrowser());
            }
        }
Пример #8
0
        public void HandleCommand(ByteReader data)
        {
            ScheduledCommand cmd = ScheduledCommand.Deserialize(data);

            cmd.issuedBySelf = data.ReadBool();
            OnMainThread.ScheduleCommand(cmd);
            Multiplayer.session.localCmdId++;
            Multiplayer.session.ProcessTimeControl();
        }
Пример #9
0
        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;
                    }
                }
            }
        }
Пример #10
0
        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");
        }
Пример #11
0
        public override void DoWindowContents(Rect inRect)
        {
            Text.Font = GameFont.Small;

            Text.Anchor = TextAnchor.UpperCenter;
            Widgets.Label(new Rect(0, 0, inRect.width, 40), $"{"MpDesynced".Translate()}\n{text}");
            Text.Anchor = TextAnchor.UpperLeft;

            float buttonWidth = 120 * 4 + 10 * 3;
            var   buttonRect  = new Rect((inRect.width - buttonWidth) / 2, 40, buttonWidth, 35);

            GUI.BeginGroup(buttonRect);

            float x = 0;

            if (Widgets.ButtonText(new Rect(x, 0, 120, 35), "MpTryResync".Translate()))
            {
                TickPatch.skipToTickUntil    = true;
                TickPatch.skipTo             = 0;
                TickPatch.afterSkip          = () => Multiplayer.Client.Send(Packets.Client_WorldReady);
                Multiplayer.session.desynced = false;

                ClientJoiningState.ReloadGame(OnMainThread.cachedMapData.Keys.ToList(), false);
            }
            x += 120 + 10;

            if (Widgets.ButtonText(new Rect(x, 0, 120, 35), "Save".Translate()))
            {
                Find.WindowStack.Add(new Dialog_SaveReplay());
            }
            x += 120 + 10;

            if (Widgets.ButtonText(new Rect(x, 0, 120, 35), "MpChatButton".Translate()))
            {
                Find.WindowStack.Add(new ChatWindow()
                {
                    closeOnClickedOutside = true, absorbInputAroundWindow = true
                });
            }
            x += 120 + 10;

            if (Widgets.ButtonText(new Rect(x, 0, 120, 35), "Quit".Translate()))
            {
                OnMainThread.StopMultiplayer();
                GenScene.GoToMainMenu();
            }

            GUI.EndGroup();
        }
Пример #12
0
        public static void HostServer(ServerSettings settings, bool fromReplay, bool withSimulation = false, bool debugMode = false, bool logDesyncTraces = false)
        {
            Log.Message($"Starting the server");

            var session = Multiplayer.session = new MultiplayerSession();

            session.myFactionId   = Faction.OfPlayer.loadID;
            session.localSettings = settings;
            session.gameName      = settings.gameName;

            var localServer = new MultiplayerServer(settings);

            if (withSimulation)
            {
                localServer.savedGame = GZipStream.CompressBuffer(OnMainThread.cachedGameData);
                localServer.mapData   = OnMainThread.cachedMapData.ToDictionary(kv => kv.Key, kv => GZipStream.CompressBuffer(kv.Value));
                localServer.mapCmds   = OnMainThread.cachedMapCmds.ToDictionary(kv => kv.Key, kv => kv.Value.Select(c => c.Serialize()).ToList());
            }
            else
            {
                OnMainThread.ClearCaches();
            }

            localServer.debugMode         = debugMode;
            localServer.debugOnlySyncCmds = new HashSet <int>(Sync.handlers.Where(h => h.debugOnly).Select(h => h.syncId));
            localServer.hostOnlySyncCmds  = new HashSet <int>(Sync.handlers.Where(h => h.hostOnly).Select(h => h.syncId));
            localServer.hostUsername      = Multiplayer.username;
            localServer.coopFactionId     = Faction.OfPlayer.loadID;

            localServer.rwVersion      = session.mods.remoteRwVersion = VersionControl.CurrentVersionString;
            localServer.modNames       = session.mods.remoteModNames = LoadedModManager.RunningModsListForReading.Select(m => m.Name).ToArray();
            localServer.modIds         = session.mods.remoteModIds = LoadedModManager.RunningModsListForReading.Select(m => m.PackageId).ToArray();
            localServer.workshopModIds = session.mods.remoteWorkshopModIds = ModManagement.GetEnabledWorkshopMods().ToArray();
            localServer.defInfos       = session.mods.defInfo = Multiplayer.localDefInfos;
            Log.Message($"MP Host modIds: {string.Join(", ", localServer.modIds)}");
            Log.Message($"MP Host workshopIds: {string.Join(", ", localServer.workshopModIds)}");

            if (settings.steam)
            {
                localServer.NetTick += SteamIntegration.ServerSteamNetTick;
            }

            if (fromReplay)
            {
                localServer.gameTimer = TickPatch.Timer;
            }

            MultiplayerServer.instance = localServer;
            session.localServer        = localServer;

            if (!fromReplay)
            {
                SetupGame();
            }

            foreach (var tickable in TickPatch.AllTickables)
            {
                tickable.Cmds.Clear();
            }

            Find.PlaySettings.usePlanetDayNightSystem = false;

            Multiplayer.RealPlayerFaction = Faction.OfPlayer;
            localServer.playerFactions[Multiplayer.username] = Faction.OfPlayer.loadID;

            SetupLocalClient();

            Find.MainTabsRoot.EscapeCurrentTab(false);

            Multiplayer.session.AddMsg("If you are having a issue with the mod and would like some help resolving it, then please reach out to us on our discord server:", false);
            Multiplayer.session.AddMsg(new ChatMsg_Url("https://discord.gg/S4bxXpv"), false);

            if (withSimulation)
            {
                StartServerThread();
            }
            else
            {
                var timeSpeed = TimeSpeed.Paused;

                Multiplayer.WorldComp.TimeSpeed = timeSpeed;
                foreach (var map in Find.Maps)
                {
                    map.AsyncTime().TimeSpeed = timeSpeed;
                }
                Multiplayer.WorldComp.UpdateTimeSpeed();

                Multiplayer.WorldComp.debugMode       = debugMode;
                Multiplayer.WorldComp.logDesyncTraces = logDesyncTraces;

                LongEventHandler.QueueLongEvent(() =>
                {
                    SaveLoad.CacheGameData(SaveLoad.SaveAndReload());
                    SaveLoad.SendCurrentGameData(false);

                    StartServerThread();
                }, "MpSaving", false, null);
            }

            void StartServerThread()
            {
                var netStarted = localServer.StartListeningNet();
                var lanStarted = localServer.StartListeningLan();

                string text = "Server started.";

                if (netStarted != null)
                {
                    text += (netStarted.Value ? $" Direct at {settings.bindAddress}:{localServer.NetPort}." : " Couldn't bind direct.");
                }

                if (lanStarted != null)
                {
                    text += (lanStarted.Value ? $" LAN at {settings.lanAddress}:{localServer.LanPort}." : " Couldn't bind LAN.");
                }

                session.serverThread = new Thread(localServer.Run)
                {
                    Name = "Local server thread"
                };
                session.serverThread.Start();

                Messages.Message(text, MessageTypeDefOf.SilentInput, false);
                Log.Message(text);
            }
        }
Пример #13
0
        public void HandleJoinData(ByteReader data)
        {
            Multiplayer.session.gameName = data.ReadString();
            Multiplayer.session.playerId = data.ReadInt32();

            var remoteInfo = new RemoteData
            {
                remoteRwVersion = data.ReadString(),
                remoteMpVersion = data.ReadString(),
                remoteAddress   = Multiplayer.session.address,
                remotePort      = Multiplayer.session.port,
                remoteSteamHost = Multiplayer.session.steamHost
            };

            var defDiff  = false;
            var defsData = new ByteReader(data.ReadPrefixedBytes());

            foreach (var local in MultiplayerData.localDefInfos)
            {
                var status = (DefCheckStatus)defsData.ReadByte();
                local.Value.status = status;

                if (status != DefCheckStatus.Ok)
                {
                    defDiff = true;
                }
            }

            JoinData.ReadServerData(data.ReadPrefixedBytes(), remoteInfo);

            OnMainThread.Schedule(Complete, 0.3f);

            void Complete()
            {
                if (JoinData.CompareToLocal(remoteInfo) && !defDiff)
                {
                    StartDownloading();
                    return;
                }

                if (defDiff)
                {
                    Multiplayer.StopMultiplayer();
                }

                var connectingWindow = Find.WindowStack.WindowOfType <BaseConnectingWindow>();

                MpUI.ClearWindowStack();

                var defDiffStr = "\n\n" + MultiplayerData.localDefInfos
                                 .Where(kv => kv.Value.status != DefCheckStatus.Ok)
                                 .Take(10)
                                 .Join(kv => $"{kv.Key}: {kv.Value.status}", "\n");

                Find.WindowStack.Add(new JoinDataWindow(remoteInfo)
                {
                    connectAnywayDisabled = defDiff ? "MpMismatchDefsDiff".Translate() + defDiffStr : null,
                    connectAnywayCallback = () =>
                    {
                        Find.WindowStack.Add(connectingWindow);
                        StartDownloading();
                    }
                });

                void StartDownloading()
                {
                    connection.Send(Packets.Client_WorldRequest);
                    subState = JoiningState.Waiting;
                }
            }
        }
Пример #14
0
        /// <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;
                    }
                }
            }
        }
Пример #15
0
        public static void HostServer(ServerSettings settings, bool fromReplay)
        {
            Log.Message($"Starting the server");

            OnMainThread.ClearCaches();

            var session = Multiplayer.session = new MultiplayerSession();

            session.myFactionId   = Faction.OfPlayer.loadID;
            session.localSettings = settings;
            session.gameName      = settings.gameName;

            var localServer = new MultiplayerServer(settings);

            localServer.debugOnlySyncCmds = new HashSet <int>(Sync.handlers.Where(h => h.debugOnly).Select(h => h.SyncId));
            localServer.hostUsername      = Multiplayer.username;
            localServer.coopFactionId     = Faction.OfPlayer.loadID;

            if (settings.steam)
            {
                localServer.NetTick += SteamIntegration.ServerSteamNetTick;
            }

            if (fromReplay)
            {
                localServer.gameTimer = TickPatch.Timer;
            }

            MultiplayerServer.instance = localServer;
            session.localServer        = localServer;

            if (!fromReplay)
            {
                SetupGame();
            }

            Find.PlaySettings.usePlanetDayNightSystem = false;

            Multiplayer.RealPlayerFaction = Faction.OfPlayer;
            localServer.playerFactions[Multiplayer.username] = Faction.OfPlayer.loadID;

            SetupLocalClient();

            Find.MainTabsRoot.EscapeCurrentTab(false);

            Multiplayer.session.AddMsg("Wiki on desyncs:");
            Multiplayer.session.AddMsg(new ChatMsg_Url("https://github.com/Zetrith/Multiplayer/wiki/Desyncs"));
            Multiplayer.session.hasUnread = false;

            LongEventHandler.QueueLongEvent(() =>
            {
                SaveLoad.CacheGameData(SaveLoad.SaveAndReload());
                SaveLoad.SendCurrentGameData(false);

                localServer.StartListening();

                session.serverThread = new Thread(localServer.Run)
                {
                    Name = "Local server thread"
                };
                session.serverThread.Start();

                string text = "Server started.";
                if (settings.bindAddress != null)
                {
                    text += $" Bound to {settings.bindAddress}:{localServer.NetPort}.";
                }
                if (settings.lanAddress != null)
                {
                    text += $" LAN at {settings.lanAddress}:{localServer.LanPort}.";
                }

                Messages.Message(text, MessageTypeDefOf.SilentInput, false);
                Log.Message(text);
            }, "MpSaving", false, null);
        }