public ChunkMChat CreateMChat(GhostNetFrame frame, string text, string tag = null, Color?color = null, bool fillVars = false, uint?id = null) { lock (ChatLog) { ChunkMChat chunk = new ChunkMChat { ID = id ?? (uint)ChatLog.Count, Text = fillVars ? FillVariables(text, frame) : text, Tag = tag ?? "", Color = color ?? GhostNetModule.Settings.ServerColorDefault, Date = DateTime.UtcNow, CreatedByServer = true, Logged = true }; if (id == null) { ChatLog.Add(chunk); } else { ChatLog[(int)id] = chunk; } return(chunk); } }
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 override void SendUpdate(GhostNetFrame frame, IPEndPoint remote, bool release) { throw new NotSupportedException("Local connections don't support sending updates to another client."); }
public override void SendUpdate(GhostNetFrame frame, bool release) { ReceiveUpdate(UpdateEndPoint, (GhostNetFrame)frame.Clone()); }
public override void SendManagement(GhostNetFrame frame, bool release) { ReceiveManagement(ManagementEndPoint, (GhostNetFrame)frame.Clone()); }
public void RunKevinballMatch() { if (KevinballPlayerIDs.Count < 2) { return; } if (ActiveKevinballMatch) { return; } uint player1 = KevinballP1; uint player2 = KevinballP2; ActiveKevinballMatch = true; if (!PlayerMap.ContainsKey(player1) || !PlayerMap.ContainsKey(player2) || !KevinballScores.ContainsKey(player1) || !KevinballScores.ContainsKey(player2)) { return; } string p1Name = PlayerMap[player1].Name; string p2Name = PlayerMap[player2].Name; string p1Score = KevinballScores[player1].X.ToString() + " - " + KevinballScores[player1].Y.ToString(); string p2Score = KevinballScores[player2].X.ToString() + " - " + KevinballScores[player2].Y.ToString(); //BroadcastMChat(new GhostNetFrame //{ // HHead = new ChunkHHead // { // PlayerID = uint.MaxValue; //}, //}, "Starting Kevinball!" + p1Name + " [" + p1Score + "] vs. " + p2Name + " [" + p2Score + "]"); string finalString = "Starting Kevinball! " + p1Name + " [" + p1Score + "] vs. " + p2Name + " [" + p2Score + "]"; BroadcastMChat(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }, finalString); GhostNetFrame frame = new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }; ChunkMKevinballStart chunk = new ChunkMKevinballStart { Player1 = player1, Player2 = player2 }; frame.Add(chunk); PropagateM(frame); //Send Kevinball start chunk with both player ids }
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) { if (FirstKevinballMatch) { GhostNetFrame shuffle_frame = new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }; uint randomLevel = PickRandomLevel(); CurrentKevinballLevel = randomLevel; ChunkMKevinballShuffle chunk = new ChunkMKevinballShuffle { NextLevel = randomLevel }; shuffle_frame.Add(chunk); PropagateM(shuffle_frame); FirstKevinballMatch = false; } StartKevinball(con, 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 abstract void SendUpdate(GhostNetFrame frame, bool release, bool log = false);
public string FillVariables(string input, GhostNetFrame frame) => input .Replace("((player))", frame.MPlayer.Name) .Replace("((id))", frame.HHead.PlayerID.ToString()) .Replace("((server))", GhostNetModule.Settings.ServerNameAuto);
protected virtual void HandleDisconnect(GhostNetConnection con) { if (!ConnectionMap.TryGetValue(con.ManagementEndPoint, out con) || 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; if (KevinballPlayerIDs.Contains(id)) { KevinballPlayerIDs.Remove(id); } if (KevinballScores.ContainsKey(id)) { KevinballScores.Remove(id); } if (KevinballQueue.Contains(id)) { KevinballQueue.Remove(id); } 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); }
protected virtual void HandleU(GhostNetConnection conReceived, IPEndPoint remote, GhostNetFrame frame, bool log) { // Prevent UpdateConnection locking in on a single player. if (conReceived == UpdateConnection) { UpdateConnection.UpdateEndPoint = null; } GhostNetConnection con; // We receive updates either from LocalConnectionToServer or from UpdateConnection. // Get the managed connection to the remote client. if (conReceived == null || !ConnectionMap.TryGetValue(remote, out con) || con == null) { // Unlike management connections, which we already know the target port of at the time of connection, // updates are sent via UDP (by default) and thus "connectionless." // If we've got a queued connection for that address, update it. GhostNetConnection queue; if (UpdateConnectionQueue.TryGetValue(remote.Address, out queue) && queue != null) { con = queue; con.UpdateEndPoint = remote; ConnectionMap[con.UpdateEndPoint] = con; Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Mapped update connection ({con.ManagementEndPoint}, {con.UpdateEndPoint})"); } else { // If the address is completely unknown, drop the frame. Logger.Log(LogLevel.Verbose, "ghostnet-s", $"Unknown update from {remote} - unknown connection, possibly premature"); return; } } Handle(con, frame); }
public void EndKevinballMatch(uint winner, uint loser, uint wintype) { ActiveKevinballMatch = false; LastKevinballWinner = winner; LastKevinballLoser = loser; KevinballScores[winner] = new Vector2(KevinballScores[winner].X + 1, KevinballScores[winner].Y); KevinballScores[loser] = new Vector2(KevinballScores[loser].X, KevinballScores[loser].Y + 1); if (!PlayerMap.ContainsKey(winner)) { return; } string finalString = PlayerMap[winner].Name + " wins!"; if (wintype == GhostNetClient.KevinballWin) { finalString = finalString + " GOOOOAAAAALLLL!!"; } else if (wintype == GhostNetClient.CoinWin) { finalString = finalString + " Coin victory!"; } else if (wintype == GhostNetClient.DeathWin) { finalString = finalString + " Survival victory!"; } BroadcastMChat(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }, finalString); GhostNetFrame frame = new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }; bool shouldShuffle = false; if (ShuffleMode > 0) { foreach (KeyValuePair <uint, Vector2> kvp in KevinballScores) { if (kvp.Value.X >= ShuffleMode) { shouldShuffle = true; string name = PlayerMap[kvp.Key].Name; string str = name + " wins the map! Shuffling maps..."; BroadcastMChat(new GhostNetFrame { HHead = new ChunkHHead { PlayerID = uint.MaxValue } }, str); } } if (shouldShuffle == true) { CurrentKevinballLevel = PickRandomLevel(); justShuffled = true; } } if (shouldShuffle) { List <uint> keys = KevinballScores.Keys.ToList(); foreach (uint key in keys) { KevinballScores[key] = new Vector2(0, 0); } } ChunkMKevinballEnd chunk = new ChunkMKevinballEnd { Winner = winner, Wintype = wintype, NextLevel = CurrentKevinballLevel }; frame.Add(chunk); PropagateM(frame); }
public abstract void SendUpdate(GhostNetFrame frame, IPEndPoint remote, bool release);
public override void SendManagement(GhostNetFrame frame, bool release) { lock (ManagementQueue) { ManagementQueue.Enqueue(Tuple.Create(frame, release)); } }
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); } if (frame.Has <ChunkUTouchPress>()) { HandleUTouchPress(con, frame); } if (frame.Has <ChunkUControlPress>()) { HandleUControlPress(con, frame); } if (frame.Has <ChunkUCrushHit>()) { HandleUCrushHit(con, frame); } if (frame.Has <ChunkUSpikeTrigger>()) { HandleUSpikeTrigger(con, frame); } if (frame.Has <ChunkUPlayerDeath>()) { HandleUPlayerDeath(con, frame); } if (frame.Has <ChunkUPickupKevinRefill>()) { HandleUKevinRefill(con, frame); } if (frame.Has <ChunkULoadedKevinball>()) { HandleULoadedKevinball(con, frame); } if (frame.Has <ChunkUKevinballWin>()) { HandleUWonKevinball(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 abstract void SendManagement(GhostNetFrame frame, bool release);