public void Move(uint playerID, int index, GhostNetFrame frame = null) { Logger.Log(LogLevel.Verbose, "ghostnet-race", $"Moving player {playerID} to index {index}"); GhostNetConnection con = Manager.Server.Connections[(int)playerID]; ChunkMPlayer playerCached; if (con == null || !Manager.Server.PlayerMap.TryGetValue(playerID, out playerCached) || playerCached == null) { return; } Indices[playerID] = index; if (index == -1 && WaitingForStart.Contains(playerID) && string.IsNullOrEmpty(playerCached.SID)) { WaitingForStart.Remove(playerID); return; } ChunkMPlayer player = new ChunkMPlayer { Name = playerCached.Name, SID = index == -1 ? "" : Areas[index].Item1, Mode = index == -1 ? AreaMode.Normal : Areas[index].Item2, Level = "" }; if (frame == null) { con.SendManagement(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = playerID }, MPlayer = player }, true); } else { frame.MPlayer = player; frame.PropagateM = true; } }
public virtual void HandleULoadedKevinball(GhostNetConnection con, GhostNetFrame frame) { ChunkULoadedKevinball press = frame; ChunkMPlayer otherPlayer; if (!PlayerMap.TryGetValue(press.With, out otherPlayer) || otherPlayer == null || frame.MPlayer.SID != otherPlayer.SID || frame.MPlayer.Mode != otherPlayer.Mode ) { // Player not in the same room. return; } if (!KevinballPlayerIDs.Contains(press.With)) { KevinballPlayerIDs.Add(press.With); KevinballQueue.Add(press.With); //TESTING //KevinballPlayerIDs.Add(press.With); //KevinballQueue.Add(press.With); } if (!KevinballScores.ContainsKey(press.With)) { KevinballScores.Add(press.With, new Vector2(0, 0)); } if (ActiveKevinballMatch) { return; } if (KevinballPlayerIDs.Count - SpectatingPlayers.Count >= 2) { StartKevinball(con, frame); } }
public ChunkMChat Send(GhostNetFrame frame, string text, string tag = "race", Color?color = null, bool fillVars = false, uint?id = null) { ChunkMChat msg = Manager.Server.CreateMChat(frame, text, tag, color ?? (frame != null ? ColorDefault : ColorBroadcast), fillVars, id); GhostNetFrame frameMsg = new GhostNetFrame { frame?.HHead ?? new ChunkHHead { PlayerID = uint.MaxValue }, msg }; lock (Players) { foreach (uint playerID in Players) { GhostNetConnection con = Manager.Server.Connections[(int)playerID]; if (con == null) { continue; } con.SendManagement(frameMsg, false); } } return(msg); }
public virtual void Accept(GhostNetConnection con) { uint id = (uint)Connections.Count; Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Client #{id} ({con.ManagementEndPoint}) accepted"); Connections.Add(con); ConnectionMap[con.ManagementEndPoint] = con; UpdateConnectionQueue[con.ManagementEndPoint.Address] = con; if (GhostNetModule.Settings.ServerRemoteOpIPs.Contains(con.ManagementEndPoint.Address.ToString())) { OPs.Add(id); } con.SendManagement(new GhostNetFrame { new ChunkHHead { PlayerID = id }, new ChunkMServerInfo { Name = GhostNetModule.Settings.ServerNameAuto }, new ChunkMRequest { ID = ChunkMPlayer.ChunkID } }, true); }
protected virtual void HandleDisconnect(GhostNetConnection con) { if (con == null) { return; // Probably already disconnected. } uint id = (uint)Connections.IndexOf(con); if (id == uint.MaxValue) { Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Client #? ({con.ManagementEndPoint}) disconnected?"); return; } Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Client #{id} ({con.ManagementEndPoint}) disconnected"); Connections[(int)id] = null; ConnectionMap[con.ManagementEndPoint] = null; if (con.UpdateEndPoint != null) { ConnectionMap[con.UpdateEndPoint] = null; } else { UpdateConnectionQueue[con.ManagementEndPoint.Address] = null; } ChunkMPlayer player; if (PlayerMap.TryGetValue(id, out player) && player != null && !string.IsNullOrWhiteSpace(player.Name) && !string.IsNullOrWhiteSpace(GhostNetModule.Settings.ServerMessageLeave)) { BroadcastMChat(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = id }, MPlayer = player }, GhostNetModule.Settings.ServerMessageLeave, fillVars: true); } OnDisconnect?.Invoke(id, player); // Propagate disconnect to all other players. GhostNetFrame frame = new GhostNetFrame { HHead = new ChunkHHead { PlayerID = id }, MPlayer = new ChunkMPlayer { Name = "", SID = "", Mode = AreaMode.Normal, Level = "" } }; lock (PlayerMap) { PlayerMap[id] = null; } PropagateM(frame); }
public virtual void HandleMChat(GhostNetConnection con, GhostNetFrame frame) { ChunkMChat msg = frame; msg.Text = msg.Text.TrimEnd(); // Logger.Log(LogLevel.Info, "ghostnet-s", $"#{frame.HHead.PlayerID} said: {frame.MChat.Text}"); if (!msg.Logged) { lock (ChatLog) { msg.ID = (uint)ChatLog.Count; ChatLog.Add(msg); } } // Handle commands if necessary. if (msg.Text.StartsWith(GhostNetModule.Settings.ServerCommandPrefix)) { // Echo the chat chunk separately. msg.Color = GhostNetModule.Settings.ServerColorCommand; con.SendManagement(new GhostNetFrame { frame.HHead, msg }, true); GhostNetCommandEnv env = new GhostNetCommandEnv { Server = this, Connection = con, Frame = frame }; string prefix = GhostNetModule.Settings.ServerCommandPrefix; // TODO: This is basically a port of disbot-neo's Handler. string cmdName = env.Text.Substring(prefix.Length); cmdName = cmdName.Split(GhostNetCommand.CommandNameDelimiters)[0].ToLowerInvariant(); if (cmdName.Length == 0) { return; } GhostNetCommand cmd = GetCommand(cmdName); if (cmd != null) { GhostNetFrame cmdFrame = frame; Task.Run(() => { try { cmd.Parse(env); } catch (Exception e) { SendMChat(con, cmdFrame, $"Command {cmdName} failed: {e.Message}", color: GhostNetModule.Settings.ServerColorError, fillVars: false); if (e.GetType() != typeof(Exception)) { Logger.Log(LogLevel.Warn, "ghostnet-s", $"cmd failed: {env.Text}"); e.LogDetailed(); } } }); } else { SendMChat(con, frame, $"Command {cmdName} not found!", color: GhostNetModule.Settings.ServerColorError, fillVars: false); } return; } if (!msg.CreatedByServer) { msg.Text.Replace("\r", "").Replace("\n", ""); if (msg.Text.Length > GhostNetModule.Settings.ServerMaxChatTextLength) { msg.Text = msg.Text.Substring(0, GhostNetModule.Settings.ServerMaxChatTextLength); } msg.Tag = ""; msg.Color = Color.White; } msg.Date = DateTime.UtcNow; frame.PropagateM = true; }
public virtual void Handle(GhostNetConnection con, GhostNetFrame frame) { SetNetHead(con, frame); if (frame.HHead == null) { return; } bool lockedMPlayer = false; if (frame.MPlayer != null) { Monitor.Enter(frame.MPlayer, ref lockedMPlayer); frame.MPlayer.IsCached = false; HandleMPlayer(con, frame); } ChunkMPlayer player; if (!PlayerMap.TryGetValue(frame.HHead.PlayerID, out player) || player == null) { // Ghost not managed - ignore the frame. Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Unexpected frame from #{frame.HHead?.PlayerID.ToString() ?? "???"} ({con.ManagementEndPoint}) - no MPlayer on this connection, possibly premature"); return; } // Temporarily attach the MPlayer chunk to make player identification easier. if (frame.MPlayer == null) { frame.MPlayer = player; Monitor.Enter(frame.MPlayer, ref lockedMPlayer); frame.MPlayer.IsCached = true; } if (frame.Has <ChunkMRequest>()) { // TODO: Handle requests by client in server. frame.Remove <ChunkMRequest>(); // Prevent request from being propagated. } if (frame.Has <ChunkMEmote>()) { HandleMEmote(con, frame); } if (frame.Has <ChunkMChat>()) { HandleMChat(con, frame); } if (frame.UUpdate != null) { HandleUUpdate(con, frame); } if (frame.Has <ChunkUActionCollision>()) { HandleUActionCollision(con, frame); } // TODO: Restrict players from abusing UAudioPlay and UParticles propagation. if (frame.Has <ChunkUAudioPlay>()) { // Propagate audio to all active players in the same room. frame.PropagateU = true; } if (frame.Has <ChunkUParticles>()) { // Propagate particles to all active players in the same room. frame.PropagateU = true; } OnHandle?.Invoke(con, frame); if (frame.PropagateM) { PropagateM(frame); } else if (frame.PropagateU) { PropagateU(frame); } if (lockedMPlayer) { Monitor.Exit(frame.MPlayer); } }
public GhostNetFrame Request <T>(GhostNetConnection con, long timeout = 5000) where T : IChunk => Request(typeof(T), con, timeout);