public void PropagateU(GhostNetFrame frame) { // U is always handled after M. Even if sending this fails, we shouldn't worry about loosing M chunks. for (int i = 0; i < Connections.Count; i++) { GhostNetConnection otherCon = Connections[i]; if (otherCon == null || (!AllowLoopbackUpdates && i == frame.HHead.PlayerID)) { continue; } ChunkMPlayer otherPlayer; if (!PlayerMap.TryGetValue((uint)i, out otherPlayer) || otherPlayer == null || frame.MPlayer.SID != otherPlayer.SID || frame.MPlayer.Mode != otherPlayer.Mode ) { continue; } if (!(otherCon is GhostNetRemoteConnection)) { otherCon.SendUpdate(frame, false); } else if (otherCon.UpdateEndPoint != null && !GhostNetModule.Settings.SendUFramesInMStream) { UpdateConnection.SendUpdate(frame, otherCon.UpdateEndPoint, false); } else { // Fallback for UDP-less clients. otherCon.SendManagement(frame, false); } } }
public ChunkMChat SendMChat(GhostNetConnection con, GhostNetFrame frame, string text, string tag = null, Color?color = null, bool fillVars = false) { ChunkMChat msg = CreateMChat(frame, text, tag, color, fillVars); con.SendManagement(new GhostNetFrame { new ChunkHHead { PlayerID = uint.MaxValue }, msg }, true); return(msg); }
public void PropagateM(GhostNetFrame frame) { for (int i = 0; i < Connections.Count; i++) { GhostNetConnection otherCon = Connections[i]; if (otherCon == null) { continue; } otherCon.SendManagement(frame, false); } }
public virtual void HandleMPlayer(GhostNetConnection con, GhostNetFrame frame) { frame.MPlayer.Name = frame.MPlayer.Name.Replace("*", "").Replace("\r", "").Replace("\n", "").Trim(); if (frame.MPlayer.Name.Length > GhostNetModule.Settings.ServerMaxNameLength) { frame.MPlayer.Name = frame.MPlayer.Name.Substring(0, GhostNetModule.Settings.ServerMaxNameLength); } if (string.IsNullOrWhiteSpace(frame.MPlayer.Name)) { frame.MPlayer.Name = "#" + frame.HHead.PlayerID; } // Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Received nM0 from #{frame.PlayerID} ({con.EndPoint})"); Logger.Log(LogLevel.Info, "ghostnet-s", $"#{frame.HHead.PlayerID} {frame.MPlayer.Name} in {frame.MPlayer.SID} {(char) ('A' + frame.MPlayer.Mode)} {frame.MPlayer.Level}"); // Propagate status to all other players. frame.MPlayer.IsEcho = true; frame.PropagateM = true; if (!PlayerMap.ContainsKey(frame.HHead.PlayerID)) { // Player just connected. if (!string.IsNullOrWhiteSpace(GhostNetModule.Settings.ServerMessageGreeting)) { BroadcastMChat(frame, GhostNetModule.Settings.ServerMessageGreeting, fillVars: true); SendMChat(con, frame, GhostNetModule.Settings.ServerMessageMOTD, fillVars: true); } } // Inform the player about all existing ghosts. lock (PlayerMap) { PlayerMap[frame.HHead.PlayerID] = frame.MPlayer; foreach (KeyValuePair <uint, ChunkMPlayer> otherStatus in PlayerMap) { if (otherStatus.Value == null || (!AllowLoopbackUpdates && otherStatus.Key == frame.HHead.PlayerID)) { continue; } con.SendManagement(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = otherStatus.Key }, MPlayer = otherStatus.Value.Clone() as ChunkMPlayer }, true); } } }
public GhostNetFrame Request(Type type, GhostNetConnection con, long timeout = 5000) { GhostNetFrame response = null; // Temporary handler to grab the response. GhostNetFrameHandler filter = (filterCon, filterFrame) => { if (response != null) { return; // Already received a response. } if (filterCon != con) { return; // Not the player we sent the request to. } if (!filterFrame.Has(type)) { return; // Doesn't contain the awaited response. } response = filterFrame; }; OnHandle += filter; // Send a request. con.SendManagement(new GhostNetFrame { new ChunkHHead { PlayerID = int.MaxValue, }, new ChunkMRequest { ID = GhostNetFrame.GetChunkID(type) } }, true); // Wait for the response. Stopwatch timeoutWatch = new Stopwatch(); timeoutWatch.Start(); while (response == null && timeoutWatch.ElapsedMilliseconds < timeout) { Thread.Sleep(0); } // If we still get a response after the timeout elapsed but before the handler has been removed, deal with it. OnHandle -= filter; return(response); }
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; con.SendManagement(new GhostNetFrame { new ChunkHHead { PlayerID = id }, new ChunkMServerInfo { Name = GhostNetModule.Settings.ServerNameAuto }, new ChunkMRequest { ID = ChunkMPlayer.ChunkID } }, true); }
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 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 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; }