예제 #1
0
        public void HandleDeath(Farmer whoDied, long?damagerID = null)
        {
            if (!IsGameInProgress)
            {
                return;
            }

            if (damagerID.HasValue)
            {
                foreach (Farmer killer in Game1.getOnlineFarmers())
                {
                    if (damagerID.Value == killer.UniqueMultiplayerID)
                    {
                        Console.WriteLine($"{whoDied.Name} has been killed by {killer.Name}");
                        //[124] is the sword e-mote icon
                        NetworkUtility.SendChatMessageToAllPlayers($"[peach]{killer.Name} [124] {whoDied.Name}");
                        break;
                    }
                }
            }
            else
            {
                NetworkUtility.SendChatMessageToAllPlayers($"[peach]{whoDied.Name} died");
                Console.WriteLine($"Received news that {whoDied.Name} was killed");
            }

            alivePlayers.Remove(whoDied.UniqueMultiplayerID);

            foreach (Farmer player in Game1.getAllFarmers())
            {
                if (player != Game1.player && player != null)
                {
                    Game1.server?.sendMessage(player.UniqueMultiplayerID,
                                              new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (int)NetworkUtility.MessageTypes.BROADCAST_ALIVE_COUNT, alivePlayers.Count));
                }
            }


            if (alivePlayers.Count == 1)
            {
                long winnerID = alivePlayers[0];
                foreach (Farmer player in Game1.getOnlineFarmers())
                {
                    if (player != null && player.UniqueMultiplayerID == winnerID)
                    {
                        HandleWin(player, whoDied);
                        return;
                    }
                }
                HandleWin(null, whoDied);
            }
            else if (alivePlayers.Count == 0)
            {
                HandleWin(null, whoDied);
            }
            else
            {
                NetworkUtility.SendChatMessageToAllPlayers($"'{whoDied.Name}' has died! {alivePlayers.Count} players remain...");
            }
        }
예제 #2
0
        public static bool Prefix(BasicProjectile __instance, GameLocation location, Farmer player)
        {
            bool damagesMonsters = ModEntry.BRGame.ModHelper.Reflection.GetField <NetBool>(__instance, "damagesMonsters").GetValue().Value;

            Console.WriteLine($"damage, m = {damagesMonsters}");
            if (!damagesMonsters || true && SlingshotPatch5.GetFarmerBounds(player).Intersects(__instance.getBoundingBox()))            //TODO: remove?
            {
                //TODO: modify slingshot damage here?
                int damage = __instance.damageToFarmer.Value;
                damage = Math.Min(damage, 10);

                if (player == Game1.player)
                {
                    Console.WriteLine("sending slingshot damage to self");
                    ModEntry.BRGame.TakeDamage(damage);
                }
                else
                {
                    Console.WriteLine("sending slingshot damage to other player");
                    NetworkUtility.SendDamageToPlayer(damage, player, Game1.player.UniqueMultiplayerID);
                }

                try
                {
                    if (!HitShaker.IsPlayerFlashing(player.UniqueMultiplayerID))
                    {
                        var r = ModEntry.BRGame.ModHelper.Reflection;
                        r.GetMethod(__instance, "explosionAnimation", false)?.Invoke(location);
                    }
                }
                catch (Exception) { }
            }
            return(false);
        }
예제 #3
0
        private void HandleWin(Farmer winner, Farmer whoDied)
        {
            if (whoDied == null && winner == null)
            {
                NetworkUtility.SendChatMessageToAllPlayers($"Ending game to avoid eternal wait.");
            }
            else if (winner == null)
            {
                NetworkUtility.SendChatMessageToAllPlayers($"'{whoDied.Name}' died, but no one was present to claim the victory.");
            }
            else
            {
                NetworkUtility.SendChatMessageToAllPlayers($"[orange]#1 VALLEY ROYALE '{winner.Name.ToUpper()}'");
            }

            IsGameInProgress = false;

            ClientEndGame();

            foreach (Farmer player in Game1.getOnlineFarmers())
            {
                if (player != Game1.player && player != null)
                {
                    NetworkUtility.BroadcastGameEndToClient(player);
                }
            }

            int timeBetweenRound = modConfig.TimeInSecondsBetweenRounds;

            if (timeBetweenRound >= 0)
            {
                NetworkUtility.SendChatMessageToAllPlayers($"Starting new game in {timeBetweenRound} seconds...");

                waitingForNextRoundToStart = true;
                WhenToStartNextRound       = DateTime.Now + new TimeSpan(hours: 0, minutes: 0, seconds: timeBetweenRound);
            }


            int hatID = 10;            //Chicken hat

            if (winner != null && (winner.hat == null || winner.hat.Value == null || winner.hat.Value.ParentSheetIndex != hatID) && (winner.Items == null || !winner.Items.Any(x => x is StardewValley.Objects.Hat && x.ParentSheetIndex == hatID)))
            {
                if (Game1.player == winner)
                {
                    winner.addItemToInventory(new StardewValley.Objects.Hat(hatID));
                }
                else
                {
                    var message = new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (int)NetworkUtility.MessageTypes.GIVE_HAT);
                    Game1.server?.sendMessage(winner.UniqueMultiplayerID, message);
                }
            }
        }
예제 #4
0
        public void ProcessPlayerJoin(NetFarmerRoot farmerRoot)
        {
            Console.WriteLine($"(Game) Player joining...");

            Storm.SendPhasesData(farmerRoot.Value.UniqueMultiplayerID);

            //If you run immediately it does nothing
            Task.Factory.StartNew(() =>
            {
                System.Threading.Thread.Sleep(1000);                //TODO: reduce/increase maybe?
                NetworkUtility.WarpFarmer(farmerRoot.Value, new TileLocation("Forest", 100, 20));
            });

            if (IsGameInProgress && alivePlayers.Count <= 1)
            {
                //Restart the game, or nothing will ever happen
                HandleWin(null, null);
            }
        }
        public static void Prefix(Microsoft.Xna.Framework.Rectangle areaOfEffect,
                                  ref int minDamage,
                                  ref int maxDamage,
                                  bool isBomb,
                                  float knockBackModifier,
                                  int addedPrecision,
                                  float critChance,
                                  float critMultiplier,
                                  bool triggerMonsterInvincibleTimer,
                                  Farmer who,
                                  NetCollection <NPC> ___characters, GameLocation __instance)
        {
            //Console.WriteLine($"{who.Name} attacked with {minDamage} to {maxDamage} damage");

            var players = Game1.getOnlineFarmers().Where(x => x != who && x.currentLocation == who.currentLocation);

            foreach (Farmer player in players)
            {
                if (player.GetBoundingBox().Intersects(areaOfEffect))
                {
                    if (maxDamage >= 0)
                    {
                        int damageAmount = Game1.random.Next(minDamage, maxDamage + 1);

                        bool crit = false;
                        if (who != null && Game1.random.NextDouble() < (double)(critChance + (float)who.LuckLevel * (critChance / 40f)))                        //Change?
                        {
                            crit = true;
                            __instance.playSound("crit");
                        }
                        damageAmount = (crit ? ((int)((float)damageAmount * critMultiplier)) : damageAmount);
                        damageAmount = Math.Max(1, damageAmount + ((who != null) ? (who.attack * 3) : 0));

                        Console.WriteLine($"Calculated {damageAmount} damage to deal to {player.Name}");

                        NetworkUtility.SendDamageToPlayer(damageAmount, player, Game1.player.UniqueMultiplayerID);
                    }
                }
            }
        }
예제 #6
0
        public void ServerStartGame()
        {
            if (IsGameInProgress)
            {
                Console.WriteLine("A game is already in progress!");
                return;
            }
            IsGameInProgress = true;

            Console.WriteLine("Server start game");


            NetworkUtility.SendChatMessageToAllPlayers("Game starting!");

            //Wipe objects
            foreach (GameLocation location in Game1.locations)
            {
                location.Objects.Clear();
                location.debris.Clear();
            }


            Game1.MasterPlayer.Money = 0;

            //Spawn chests
            new Chests().SpawnAndFillChests();

            #region Store tree locations or replant
            Random random = new Random();
            if (trees.Count == 0)            //Store info
            {
                foreach (GameLocation gameLocation in Game1.locations)
                {
                    foreach (var pair in gameLocation.terrainFeatures.Pairs)
                    {
                        TerrainFeature feature = pair.Value;
                        if (feature is Tree tree && tree.growthStage.Value >= 5)
                        {
                            Vector2 vector = pair.Key;
                            trees.Add(new TileLocation(gameLocation.Name, (int)vector.X, (int)vector.Y));
                        }
                    }
                }
            }
            else            //Replant
            {
                //Delete all trees
                foreach (GameLocation gameLocation in Game1.locations)
                {
                    List <Vector2> toRemove = new List <Vector2>();
                    foreach (Vector2 vector in gameLocation.terrainFeatures.Keys)
                    {
                        TerrainFeature feature = gameLocation.terrainFeatures[vector];
                        if (feature is Tree tree && tree.growthStage.Value >= 5)
                        {
                            toRemove.Add(vector);
                        }
                    }

                    foreach (Vector2 vector in toRemove)
                    {
                        gameLocation.terrainFeatures.Remove(vector);
                    }
                }

                //Replant
                foreach (TileLocation treeLocation in trees)
                {
                    treeLocation.GetGameLocation().terrainFeatures.Remove(treeLocation.CreateVector2());
                    treeLocation.GetGameLocation().terrainFeatures.Add(treeLocation.CreateVector2(), new Tree(random.Next(1, 4), 100));
                }
            }
            #endregion

            //Remove resource clumps (e.g. boulders) other than stumps and hollow logs
            var rc = Game1.getFarm().resourceClumps;
            foreach (var a in rc.Where(a => a.parentSheetIndex.Value != 600 || a.parentSheetIndex.Value != 602).ToList())
            {
                rc.Remove(a);
            }

            //Remove small stones and weeds
            var farmObjects = Game1.getFarm().objects;
            var keys        = farmObjects.Keys.ToList();
            for (int i = 0; i < keys.Count(); i++)
            {
                var key0 = keys[i];

                if (farmObjects[key0].name == "Stone" || farmObjects[key0].name == "Weeds")
                {
                    farmObjects.Remove(key0);
                }
            }

            //Setup alive players
            alivePlayers.Clear();
            foreach (Farmer player in Game1.getOnlineFarmers())
            {
                if (player != null && !(player == Game1.player && !modConfig.ShouldHostParticipate))
                {
                    Console.WriteLine($"Adding {player.Name} to the game");
                    alivePlayers.Add(player.UniqueMultiplayerID);
                }
            }

            //Storm index
            int stormIndex = Storm.GetRandomStormIndex();

            //Spawn players in & Tell the clients to start game
            var chosenSpawns = new Spawns().ScatterPlayers(Game1.getOnlineFarmers());
            foreach (Farmer player in chosenSpawns.Keys)
            {
                if (player == Game1.player)
                {
                    ClientStartGame(alivePlayers.Count, modConfig.ShouldHostParticipate, stormIndex);

                    if (modConfig.ShouldHostParticipate)
                    {
                        NetworkUtility.WarpFarmer(player, chosenSpawns[player]);
                    }
                    else
                    {
                        player.warpFarmer(new TileLocation("Forest", 100, 20).CreateWarp());
                    }
                }
                else
                {
                    NetworkUtility.BroadcastGameStartToClient(player, alivePlayers.Count, modConfig.ShouldHostParticipate, stormIndex);
                    NetworkUtility.WarpFarmer(player, chosenSpawns[player]);
                }
            }

            //Remove horses/NPCs
            foreach (GameLocation loc in Game1.locations)
            {
                foreach (NPC horse in loc.characters.Where(x => ModEntry.Config.KillAllNPCs || x is StardewValley.Characters.Horse).ToList())
                {
                    loc.characters.Remove(horse);
                }
            }

            //Freeze time
            Game1.timeOfDay       = 1400;
            FreezeTime.TimeFrozen = true;
        }
예제 #7
0
        public void TakeDamage(int damage, long?damagerID = null)
        {
            if (ModEntry.BRGame.IsGameInProgress)
            {
                dummyMonster = dummyMonster ?? new StardewValley.Monsters.Monster();

                try
                {
                    bool oldIsEating = Game1.player.isEating;
                    Game1.player.isEating = false;                        //Prevent invincibility

                    Game1.player.takeDamage(damage, false, dummyMonster); //If you pass in null as monster the player won't take damage

                    Game1.player.isEating = oldIsEating;
                }catch (Exception)
                {
                    Console.WriteLine("Ignoring exception in takeDamage");
                }

                Farmer damager = null;
                if (Game1.currentLocation != null && Game1.currentLocation.farmers != null)
                {
                    foreach (Farmer player in Game1.currentLocation?.farmers)
                    {
                        //Hit shake timer / Invincibility frames
                        if (player != Game1.player && player != null)
                        {
                            var objects = new object[] { (int)NetworkUtility.MessageTypes.TELL_PLAYER_HIT_SHAKE_TIMER, 1200 };                            //Hard coded in Farmer.MovePosition
                            if (Game1.IsServer)
                            {
                                Game1.server.sendMessage(player.UniqueMultiplayerID, new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, objects));
                            }
                            else
                            {
                                NetworkUtility.RelayMessageFromClientToAnotherPlayer(player.UniqueMultiplayerID, objects);
                            }

                            if (player.UniqueMultiplayerID == damagerID)
                            {
                                damager = player;
                            }
                        }
                    }
                }

                //Knockback (knock the player)
                if (damager != null && damage > 0 && (DateTime.Now - knockbackImmunityTimeActivated).TotalMilliseconds >= knockbackImmunityMilliseconds)
                {
                    double amount = 10 + 8 * (-1 + 2 / (1 + Math.Pow(Math.E, -0.03 * damage)));
                    amount = Math.Min(18, amount);                    //Just in case

                    Vector2 displacement = Vector2.Subtract(Game1.player.Position, damager.Position);
                    if (displacement.LengthSquared() != 0)
                    {
                        displacement.Normalize();

                        displacement.Y = displacement.Y * -1;
                        displacement   = Vector2.Multiply(displacement, (float)amount);

                        Console.WriteLine($"setting trajectory to {displacement.X},{displacement.Y}");

                        Game1.player.setTrajectory((int)displacement.X, (int)displacement.Y);
                    }
                }

                if (Game1.player.health <= 0)
                {
                    //They are dead now
                    Console.WriteLine($"I AM DEAD!");

                    Random random = new Random();

                    //Spawn their items onto the floor
                    foreach (var item in Game1.player.Items)
                    {
                        if (item != null)
                        {
                            if (!(item is Tool) || item is StardewValley.Tools.MeleeWeapon || item is StardewValley.Tools.Slingshot)
                            {
                                Debris debris = new Debris(item, Game1.player.getStandingPosition(), Game1.player.getStandingPosition() + new Microsoft.Xna.Framework.Vector2(64 * (float)(random.NextDouble() * 2 - 1), 64 * (float)(random.NextDouble() * 2 - 1)));
                                Game1.currentLocation.debris.Add(debris);
                            }
                        }
                    }



                    var oldLocation = Game1.player.currentLocation;
                    var oldPosition = new xTile.Dimensions.Location(
                        (int)Game1.player.Position.X - Game1.viewport.Width / 2,
                        (int)Game1.player.Position.Y - Game1.viewport.Height / 2);

                    Game1.player.clearBackpack();
                    Game1.player.health = maxHealth;                    //Otherwise they will go the the Doctor on the next update tick
                    //Game1.player.warpFarmer(new TileLocation("Forest", 100, 20).CreateWarp());//Marnie's cow pen

                    SpectatorMode.EnterSpectatorMode(oldLocation, oldPosition);

                    if (Game1.IsServer)
                    {
                        ModEntry.BRGame.HandleDeath(Game1.player, damagerID);
                    }
                    else
                    {
                        if (damagerID.HasValue)
                        {
                            Game1.client.sendMessage(new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player,
                                                                         (int)NetworkUtility.MessageTypes.TELL_SERVER_I_DIED, Game1.player.UniqueMultiplayerID, damagerID.Value));
                        }
                        else
                        {
                            Game1.client.sendMessage(new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player,
                                                                         (int)NetworkUtility.MessageTypes.TELL_SERVER_I_DIED, Game1.player.UniqueMultiplayerID));
                        }
                    }
                }
            }
        }
예제 #8
0
        public static void QuarterSecUpdate(List <long> alivePlayers)
        {
            if (!Game1.IsServer)
            {
                return;
            }

            //var (currentPhase, currentPhaseAmount) = GetCurrentPhase();
            var tp                 = GetCurrentPhase();
            var currentPhase       = tp.Item1;
            var currentPhaseAmount = tp.Item2;

            //var amounts = new Dictionary<GameLocation, (double amount, Direction direction, Phase.TwoPointRectangle closeInRectangle)>();
            var amounts = new Dictionary <GameLocation, Tuple <double, Direction, Phase.TwoPointRectangle> >();

            foreach (Farmer player in Game1.getOnlineFarmers())
            {
                var currentLocation = player.currentLocation;
                if (currentLocation != null && !amounts.ContainsKey(currentLocation))
                {
                    amounts.Add(currentLocation, GetStormAmountInLocation(currentPhase, currentPhaseAmount, currentLocation));
                }
            }

            foreach (var pair in amounts)
            {
                var gameLocation = pair.Key;
                //var (amount, direction, closeInRectangle) = pair.Value;
                var t                = pair.Value;
                var amount           = t.Item1;
                var direction        = t.Item2;
                var closeInRectangle = t.Item3;

                Rectangle bounds = GetStormBounds(direction, amount, gameLocation, closeInRectangle);

                foreach (Farmer farmer in gameLocation.farmers.Where(x => alivePlayers.Contains(x.UniqueMultiplayerID)))
                {
                    bool contains = bounds.Contains(Utility.Vector2ToPoint(farmer.Position));
                    if (contains && (closeInRectangle == null) || (!contains) && (closeInRectangle != null))
                    {
                        int damage = (int)(ModEntry.Config.StormDamagePerSecond * 1.2);                        //Effectively 20 damage per 1.2 sec because invincibility lasts 1.2 sec

                        if (Game1.MasterPlayer == farmer)
                        {
                            ModEntry.BRGame.TakeDamage(damage);
                        }
                        else
                        {
                            NetworkUtility.SendDamageToPlayer(damage, farmer);
                        }
                    }
                }
            }

            //When the phase has closed in, two players could not kill eachother and the game would last indefinitely.
            //When time > phasetime*1.3, damage them anyway
            if (DateTime.Now - startTime >= TimeSpan.FromTicks((long)(totalLengthOfPhases.Ticks * 2)))
            {
                int damage = 10;

                foreach (long id in ModEntry.BRGame.alivePlayers.ToList())
                {
                    if (Game1.MasterPlayer.UniqueMultiplayerID == id)
                    {
                        ModEntry.BRGame.TakeDamage(damage);
                    }
                    else
                    {
                        NetworkUtility.SendDamageToPlayerServerOnly(damage, id);
                    }
                }
            }
        }
예제 #9
0
        public override void Entry(IModHelper helper)
        {
            Events = helper.Events;

            if (AntiCheat.HasIllegalMods())
            {
                Game1.quit = true;

                Console.Clear();
                Monitor.Log("You have an illegal mod installed. Please uninstall it before using this mod.", LogLevel.Warn);
                if (AntiCheat.IllegalMod != null)
                {
                    Monitor.Log($"- '{AntiCheat.IllegalMod}'", LogLevel.Warn);
                }
                Monitor.Log("Press any key to continue...", LogLevel.Warn);
                Console.ReadKey();
                return;
            }

            var config = helper.ReadConfig <ModConfig>();

            if (config == null)
            {
                config = new ModConfig();
                helper.WriteConfig(config);
            }

            Config = config;

            BRGame = new Game(helper, config);

            Patch.PatchAll("ilyaki.battleroyale");

            try
            {
                //Remove player limit
                var multiplayer = helper.Reflection.GetField <Multiplayer>(typeof(Game1), "multiplayer").GetValue();
                multiplayer.playerLimit = config.PlayerLimit;//250 is the Galaxy limit (it's multiplied by two for some reason, so set 125)
            }catch (Exception)
            {
                Monitor.Log("Error setting player limit. The max is 125", LogLevel.Error);
            }


            helper.ConsoleCommands.Add("br_start", "Start the game. Alternative to pressing Right control", (c, a) => {
                if (Game1.IsServer)
                {
                    BRGame.ServerStartGame();
                }
            });

            Events.Input.ButtonPressed += (o, e) =>
            {
                if (Game1.IsServer && e.Button.TryGetKeyboard(out Keys key) && key == Keys.RightControl)
                {
                    BRGame.ServerStartGame();
                }

                if (e.Button.TryGetKeyboard(out Keys key2) && key2 == Keys.RightAlt)
                {
                    var loc = Game1.player.currentLocation;
                    int x   = Game1.player.getTileX();
                    int y   = Game1.player.getTileY();


                    Monitor.Log($"tile location={loc.Name}, pos=({x},{y})", LogLevel.Info);

                    Monitor.Log($"my id : {Game1.player.UniqueMultiplayerID}", LogLevel.Info);

                    Monitor.Log($"precise position = {Game1.player.Position}", LogLevel.Info);

                    /*Game1.player.addItemToInventory(new StardewValley.Tools.Slingshot());
                     * Game1.player.addItemToInventory(new StardewValley.Tools.Slingshot(33));
                     *
                     * var xd = new int[] { 388, 390, 378, 380, 384, 382, 386, 441 };
                     * foreach(int asdf in xd)
                     * {
                     *      Game1.player.addItemToInventory(new StardewValley.Object(asdf, 100));
                     * }*/
                }
            };

            Events.GameLoop.UpdateTicked += (o, e) => BRGame?.Update(Game1.currentGameTime);
            Events.GameLoop.UpdateTicked += (o, e) => HitShaker.Update(Game1.currentGameTime);
            Events.GameLoop.UpdateTicked += (o, e) => SpectatorMode.Update();

            //https://github.com/funny-snek/Always-On-Server-for-Multiplayer/blob/master/Always%20On%20Server/ModEntry.cs
            string currentSavedInviteCode = "";

            Events.GameLoop.UpdateTicked += (o, e) =>
            {
                if (e.IsMultipleOf(60 * 5) && Game1.server != null && Game1.options.enableServer == true && !string.Equals(currentSavedInviteCode, Game1.server.getInviteCode()))
                {
                    currentSavedInviteCode = Game1.server.getInviteCode();

                    try
                    {
                        //helper.DirectoryPath
                        StreamWriter sw = new StreamWriter("InviteCode.txt");                        //Should be in the same folder as StardewModdingAPI.exe
                        sw.WriteLine(currentSavedInviteCode);
                        sw.Close();
                    }
                    catch (Exception b)
                    {
                        Console.WriteLine("Exception writing InviteCode: " + b.Message);
                    }
                }
            };

            Events.Player.Warped += (o, e) =>
            {
                if (e.NewLocation != null && (e.NewLocation is StardewValley.Locations.Woods || e.NewLocation.Name == "BugLand"))
                {
                    e.NewLocation.characters.Clear();
                }
            };

            Events.Display.RenderingHud += (o, e) => {
                if (!SpectatorMode.InSpectatorMode)
                {
                    Storm.Draw(Game1.spriteBatch);
                }
            };


            Events.Display.Rendered += (o, e) =>
            {
                if (SpectatorMode.InSpectatorMode)                //Spectator mode can only see the storm when it is drawn above everything
                {
                    Storm.Draw(Game1.spriteBatch);

                    if (Game1.activeClickableMenu == null)
                    {
                        string message = "Spectating";
                        SpriteText.drawStringWithScrollBackground(Game1.spriteBatch, message, Game1.viewport.Width / 2 - SpriteText.getWidthOfString(message) / 2, 16, "", 1f, -1);
                    }
                }
            };

            helper.ConsoleCommands.Add("br_kick", "Kick a player. Usage: br_kick <player ID>", (a, b) =>
            {
                if (!Game1.IsServer)
                {
                    Monitor.Log("Need to be the server host", LogLevel.Info);
                }
                else if (b.Length != 1)
                {
                    Monitor.Log("Need 1 argument", LogLevel.Info);
                }
                else if (!long.TryParse(b[0], out long x))
                {
                    Monitor.Log("Not a valid number", LogLevel.Info);
                }
                else
                {
                    try
                    {
                        var f = Game1.getOnlineFarmers().First(p => p != Game1.player && p.UniqueMultiplayerID == x);
                        NetworkUtility.KickPlayer(f, "You have been kicked by the host.");
                    }
                    catch (Exception)
                    {
                        Monitor.Log($"Could not find player with id {x}", LogLevel.Info);
                    }
                }
            });

            helper.ConsoleCommands.Add("br_setNumberOfPlayerSlots", "Sets the number of player slots. Usage: br_setNumberOfPlayerSlots <number of slots>", (a, b) =>
            {
                if (!Game1.IsServer)
                {
                    Monitor.Log("Need to be the server host", LogLevel.Info);
                }
                else if (b.Length != 1)
                {
                    Monitor.Log("Need 1 argument", LogLevel.Info);
                }
                else if (!int.TryParse(b[0], out int n))
                {
                    Monitor.Log("Not a valid number", LogLevel.Info);
                }
                else
                {
                    n = Math.Abs(n);
                    var emptyCabins = Game1.getFarm().buildings.Where(z => z.daysOfConstructionLeft.Value <= 0 && z.indoors.Value is Cabin).ToArray();

                    if (n > emptyCabins.Length)
                    {
                        for (int i = 0; i < n - emptyCabins.Length; i++)
                        {
                            var blueprint = new BluePrint("Log Cabin");
                            var building  = new Building(blueprint, new Vector2(-10000, 0));
                            Game1.getFarm().buildings.Add(building);

                            try
                            {
                                foreach (var warp in building.indoors.Value.warps)
                                {
                                    //warp.TargetName = "Forest";
                                    Helper.Reflection.GetField <NetString>(warp, "targetName", true).SetValue(new NetString("Forest"));
                                    warp.TargetX = 100;
                                    warp.TargetY = 20;
                                }
                            }
                            catch (Exception) { }
                        }

                        Monitor.Log($"Added {n - emptyCabins.Length} player slots", LogLevel.Info);
                    }
                    else if (n < emptyCabins.Length)
                    {
                        for (int i = 0; i < emptyCabins.Length - n; i++)
                        {
                            Game1.getFarm().buildings.Remove(emptyCabins[i]);
                        }

                        Monitor.Log($"Removed {emptyCabins.Length - n} player slots", LogLevel.Info);
                    }
                    else
                    {
                        Monitor.Log($"There are already {n} player slots", LogLevel.Info);
                    }
                }
            });
        }
        private static bool ProcessMessage(byte[] msgData, Farmer sourceFarmer)
        {
            var subMessageType = (NetworkUtility.MessageTypes)BitConverter.ToInt32(msgData, 0);

            Console.WriteLine($"Receiving special message, type = {subMessageType}. Message length = {msgData.Length}");

            switch (subMessageType)
            {
            case (NetworkUtility.MessageTypes.TAKE_DAMAGE):
                int damage = BitConverter.ToInt32(msgData, 4);

                long?damagerID = null;
                if (msgData.Length > 8)
                {
                    damagerID = BitConverter.ToInt64(msgData, 8);
                }

                Console.WriteLine($"Taking {damage} damage from another player");

                ModEntry.BRGame.TakeDamage(damage, damagerID);
                return(false);

            case (NetworkUtility.MessageTypes.SERVER_BROADCAST_GAME_START):
                if (!Game1.IsServer)
                {
                    int  numberOfPlayers     = BitConverter.ToInt32(msgData, 4);
                    int  stormIndex          = BitConverter.ToInt32(msgData, 8);
                    bool isHostParticipating = BitConverter.ToBoolean(msgData, 12);

                    ModEntry.BRGame.ClientStartGame(numberOfPlayers, isHostParticipating, stormIndex);
                }
                return(false);

            case NetworkUtility.MessageTypes.WARP:
                int tileX = BitConverter.ToInt32(msgData, 4);
                int tileY = BitConverter.ToInt32(msgData, 8);

                StringBuilder sb = new StringBuilder();
                for (int i = 12; i < msgData.Length; i++)
                {
                    sb.Append((char)msgData[i]);
                }

                string locationName = sb.ToString().Substring(1);

                Console.WriteLine($"I was told by the server to move to {locationName} @ ({tileX},{tileY})");

                //Face downwards & Warp
                Game1.player.FacingDirection = 2;
                Game1.player.warpFarmer(new TileLocation(locationName, tileX, tileY).CreateWarp());
                return(false);

            case (NetworkUtility.MessageTypes.SERVER_BROADCAST_GAME_END):
                if (!Game1.IsServer)
                {
                    ModEntry.BRGame.ClientEndGame();
                }
                return(false);

            case (NetworkUtility.MessageTypes.RELAY_MESSAGE_TO_ANOTHER_PLAYER):
                if (Game1.IsServer)
                {
                    long targetUniqueID = BitConverter.ToInt64(msgData, 4);

                    byte[] messageData = new byte[msgData.Length - 12];
                    Array.Copy(msgData, 12, messageData, 0, msgData.Length - 12);

                    if (targetUniqueID == Game1.player.UniqueMultiplayerID)
                    {
                        Console.WriteLine("Received relay instruction addressed to myself");
                        ProcessMessage(messageData, sourceFarmer);
                        return(false);
                    }
                    else
                    {
                        Console.WriteLine($"Relaying a message to {targetUniqueID}...");
                        Game1.server.sendMessage(targetUniqueID, new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (object)messageData));
                        return(false);
                    }
                }
                else
                {
                    Console.WriteLine("Accidentally received relay instruction as a client");
                    return(false);
                }

            case (NetworkUtility.MessageTypes.TELL_SERVER_I_DIED):
                if (msgData.Length > 12)
                {
                    ModEntry.BRGame.HandleDeath(sourceFarmer, BitConverter.ToInt64(msgData, 12));
                }
                else
                {
                    ModEntry.BRGame.HandleDeath(sourceFarmer);
                }
                return(false);

            case NetworkUtility.MessageTypes.SERVER_BROADCAST_CHAT_MESSAGE:
                string message   = Encoding.UTF8.GetString(msgData.Skip(4).ToArray()).Substring(1);
                string colorName = "white";
                try
                {
                    colorName = message.Substring(1, message.Substring(1).IndexOf(']'));
                    Console.WriteLine($"color = {colorName}");
                }
                catch (Exception) { }

                var color = StardewValley.Menus.ChatMessage.getColorFromName(colorName);
                if (color == Microsoft.Xna.Framework.Color.White)
                {
                    color = Microsoft.Xna.Framework.Color.Gold;
                }

                Game1.chatBox.addMessage(message, color);
                return(false);

            case (NetworkUtility.MessageTypes.TELL_PLAYER_HIT_SHAKE_TIMER):
                int howLongMilliseconds = BitConverter.ToInt32(msgData, 4);
                HitShaker.SetHitShakeTimer(sourceFarmer.UniqueMultiplayerID, howLongMilliseconds);
                return(false);

            case NetworkUtility.MessageTypes.BROADCAST_ALIVE_COUNT:
                int howManyPlayersAlive = BitConverter.ToInt32(msgData, 4);

                if (ModEntry.BRGame.OverlayUI != null)
                {
                    ModEntry.BRGame.OverlayUI.AlivePlayers = howManyPlayersAlive;
                }
                return(false);

            case NetworkUtility.MessageTypes.SEND_PHASE_DATA:
                try
                {
                    var b = new BinaryFormatter();

                    var phases = (List <Phase>[])b.Deserialize(new MemoryStream(msgData.Skip(4).ToArray()));

                    Console.WriteLine($"Received phases data from server, info: Length={phases.Length}");

                    Storm.Phases = phases;
                }catch (Exception)
                {
                    Console.WriteLine("Unable to process phases data. Kicking to prevent glitches...");
                    long id = sourceFarmer.UniqueMultiplayerID;
                    NetworkUtility.SendChatMessageToPlayerWithoutMod(id, "Unable to process phases data. Kicking to prevent glitches...");
                    Game1.server.sendMessage(id, new OutgoingMessage((byte)19, id, new object[0]));
                    Game1.server.playerDisconnected(id);
                    Game1.otherFarmers.Remove(id);
                }
                return(false);

            case NetworkUtility.MessageTypes.KICK_PLAYER:
                StringBuilder sb2 = new StringBuilder();
                for (int i = 4; i < msgData.Length; i++)
                {
                    sb2.Append((char)msgData[i]);
                }

                string kickMsg = sb2.ToString().Substring(1);
                Console.WriteLine($"Kicked. Reason: \"{kickMsg}\"");

                Game1.client.disconnect();

                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    System.Threading.Thread.Sleep(300);
                    Game1.activeClickableMenu = new StardewValley.Menus.DialogueBox($"Kicked. Reason: \"{kickMsg}\"");
                });

                return(false);

            case NetworkUtility.MessageTypes.SEND_MY_VERSION_TO_SERVER:
                if (Game1.IsServer)
                {
                    int    major = BitConverter.ToInt32(msgData, 4);
                    int    minor = BitConverter.ToInt32(msgData, 8);
                    byte[] sha   = msgData.Skip(12).ToArray();

                    Console.WriteLine($"Received version from client {sourceFarmer.Name}/{sourceFarmer.UniqueMultiplayerID}: {major}.{minor}");
                    new AutoKicker().AcknowledgeClientVersion(sourceFarmer.UniqueMultiplayerID, major, minor, sha);

                    if (ModEntry.BRGame.IsGameInProgress)
                    {
                        System.Threading.Tasks.Task.Factory.StartNew(() =>
                        {
                            System.Threading.Thread.Sleep(1000);
                            Game1.server.sendMessage(sourceFarmer.UniqueMultiplayerID,
                                                     new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (int)NetworkUtility.MessageTypes.TELL_NEW_PLAYER_TO_SPECTATE));
                        });
                    }
                }
                return(false);

            case NetworkUtility.MessageTypes.TELL_NEW_PLAYER_TO_SPECTATE:
                if (!Game1.IsServer)
                {
                    System.Threading.Tasks.Task.Factory.StartNew(() =>
                    {
                        System.Threading.Thread.Sleep(1000);

                        if (!ModEntry.BRGame.alivePlayers.Contains(Game1.player.UniqueMultiplayerID))
                        {
                            var oldLocation = Game1.player.currentLocation;
                            var oldPosition = new xTile.Dimensions.Location(
                                (int)Game1.player.Position.X - Game1.viewport.Width / 2,
                                (int)Game1.player.Position.Y - Game1.viewport.Height / 2);

                            SpectatorMode.EnterSpectatorMode(oldLocation, oldPosition);
                        }
                    });
                }
                return(false);

            case NetworkUtility.MessageTypes.GIVE_HAT:
                int hatID = 10;                        //Chicken hat
                Game1.player?.addItemToInventory(new StardewValley.Objects.Hat(hatID));
                return(false);

            default:
                return(true);
            }
        }