From 644e95f3b4360f5528abce4a892a64acd3ba6a90 Mon Sep 17 00:00:00 2001 From: Date: Wed, 8 Aug 2018 00:12:31 -0700 Subject: [PATCH] Tears, sweat, and lots of code hacks later, StardustCore supports multiplayer custom objects and tools..... --- GeneralMods/StardustCore/ModCore.cs | 79 +++- .../StardustCore/NetCode/ModdedClient.cs | 165 +++++++ .../StardustCore/NetCode/ModdedGameServer.cs | 424 ++++++++++++++++++ .../NetCode/NetBufferReadStream.cs | 99 ++++ .../NetCode/NetBufferWriteStream.cs | 97 ++++ .../StardustCore/NetCode/NetCoreObject.cs | 2 +- .../NetCode/NetTexure2DExtended.cs | 68 +-- .../StardustCore/Objects/CoreObject.cs | 69 ++- .../Serialization/Serialization.cs | 6 + GeneralMods/StardustCore/StardustCore.csproj | 8 + .../UIUtilities/Texture2DExtended.cs | 2 +- .../UIUtilities/TextureManager.cs | 2 +- 12 files changed, 945 insertions(+), 76 deletions(-) create mode 100644 GeneralMods/StardustCore/NetCode/ModdedClient.cs create mode 100644 GeneralMods/StardustCore/NetCode/ModdedGameServer.cs create mode 100644 GeneralMods/StardustCore/NetCode/NetBufferReadStream.cs create mode 100644 GeneralMods/StardustCore/NetCode/NetBufferWriteStream.cs diff --git a/GeneralMods/StardustCore/ModCore.cs b/GeneralMods/StardustCore/ModCore.cs index 2008f0b5..3ac57511 100644 --- a/GeneralMods/StardustCore/ModCore.cs +++ b/GeneralMods/StardustCore/ModCore.cs @@ -3,7 +3,9 @@ using Microsoft.Xna.Framework.Input; using StardewModdingAPI; using StardewValley; using StardewValley.Menus; +using StardewValley.Network; using StardustCore.ModInfo; +using StardustCore.NetCode; using StardustCore.Objects; using StardustCore.Objects.Tools; using StardustCore.Serialization; @@ -13,6 +15,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -25,6 +28,9 @@ namespace StardustCore public static Serialization.SerializationManager SerializationManager; public static UIUtilities.TextureManager TextureManager; + public static Multiplayer multiplayer; + bool serverHack; + public static string ContentDirectory; public override void Entry(IModHelper helper) { @@ -51,18 +57,88 @@ namespace StardustCore SerializationManager.initializeDefaultSuportedTypes(); TextureManager = new TextureManager(); - + TextureManager.addTexture("Test1.png", new Texture2DExtended(ModCore.ModHelper, Path.Combine("Content", "Graphics", "MultiTest", "Test1.png"))); StardewModdingAPI.Events.ControlEvents.KeyPressed += ControlEvents_KeyPressed; StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; + serverHack = false; + + } + + /// + /// Returns the value of the data snagged by reflection. + /// + /// + /// + /// + /// + public static object GetInstanceField(Type type, object instance, string fieldName) + { + BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; + FieldInfo field = type.GetField(fieldName, bindFlags); + /* + FieldInfo[] meh = type.GetFields(bindFlags); + foreach(var v in meh) + { + if (v.Name == null) + { + continue; + } + Monitor.Log(v.Name); + } + */ + return field.GetValue(instance); + } + + public static void SetInstanceField(Type type, object instance, object value, string fieldName) + { + BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Static; + FieldInfo field = type.GetField(fieldName, bindFlags); + field.SetValue(instance, value); + return; } private void GameEvents_UpdateTick(object sender, EventArgs e) { + if (Game1.activeClickableMenu != null) + { + if(Game1.activeClickableMenu is StardewValley.Menus.TitleMenu) + { + if (TitleMenu.subMenu == null) return; + if (TitleMenu.subMenu.GetType() == typeof(FarmhandMenu)) + { + if ((TitleMenu.subMenu as FarmhandMenu).client.GetType() != typeof(ModdedClient)) + { + ModCore.ModMonitor.Log("OK!"); + multiplayer = (Multiplayer)GetInstanceField(typeof(Game1), Program.gamePtr, "multiplayer"); + ModCore.ModMonitor.Log("CODE!!!!!!!"); + string address = (string)GetInstanceField(typeof(LidgrenClient), (TitleMenu.subMenu as FarmhandMenu).client, "address"); + (TitleMenu.subMenu as FarmhandMenu).client = new NetCode.ModdedClient(address); + } + } + } + } + if (Game1.server!=null&& serverHack==false) + { + ModCore.ModMonitor.Log("OK!"); + multiplayer = (Multiplayer)GetInstanceField(typeof(Game1), Program.gamePtr, "multiplayer"); + //List servers = Helper.Reflection.GetField>(Game1.server, "servers", true).GetValue(); + NetCode.GameServer server = new NetCode.GameServer(); + Game1.server.stopServer(); + Game1.server = server; + server.startServer(); + + serverHack = true; + } + if (Game1.client !=null && serverHack == false) + { + + } } private void ControlEvents_KeyPressed(object sender, StardewModdingAPI.Events.EventArgsKeyPressed e) @@ -95,6 +171,7 @@ namespace StardustCore // Game1.player.addItemToInventory(collection); CoreObject tile1 = new CoreObject(new Texture2DExtended(ModCore.ModHelper, Path.Combine("Content", "Graphics", "MultiTest", "Test1.png")),3, Vector2.Zero,9); + tile1.description = "Hello"; tile1.Name = "test"; tile1.displayName = "test"; diff --git a/GeneralMods/StardustCore/NetCode/ModdedClient.cs b/GeneralMods/StardustCore/NetCode/ModdedClient.cs new file mode 100644 index 00000000..23f5bdcd --- /dev/null +++ b/GeneralMods/StardustCore/NetCode/ModdedClient.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using Lidgren.Network; +using StardewValley; +using StardewValley.Network; + +namespace StardustCore.NetCode +{ + public class ModdedClient : Client + { + private string address; + private NetClient client; + private bool serverDiscovered; + + public ModdedClient(string address) + { + this.address = address; + } + + public override string getUserID() + { + return ""; + } + + protected override string getHostUserName() + { + return this.client.ServerConnection.RemoteEndPoint.Address.ToString(); + } + + protected override void connectImpl() + { + NetPeerConfiguration config = new NetPeerConfiguration("StardewValley"); + config.EnableMessageType(NetIncomingMessageType.DiscoveryResponse); + config.ConnectionTimeout = 30f; + config.PingInterval = 5f; + config.MaximumTransmissionUnit = 1200; + this.client = new NetClient(config); + this.client.Start(); + int serverPort = 24642; + if (this.address.Contains(":")) + { + string[] strArray = this.address.Split(':'); + this.address = strArray[0]; + serverPort = Convert.ToInt32(strArray[1]); + } + this.client.DiscoverKnownPeer(this.address, serverPort); + } + + public override void disconnect(bool neatly = true) + { + if (this.client.ConnectionStatus != NetConnectionStatus.Disconnected && this.client.ConnectionStatus != NetConnectionStatus.Disconnecting) + { + if (neatly) + this.sendMessage(new OutgoingMessage((byte)19, Game1.player, new object[0])); + this.client.FlushSendQueue(); + this.client.Disconnect(""); + this.client.FlushSendQueue(); + } + this.connectionMessage = (string)null; + } + + protected virtual bool validateProtocol(string version) + { + return version ==ModCore.multiplayer.protocolVersion; + } + + protected override void receiveMessagesImpl() + { + NetIncomingMessage netIncomingMessage; + while ((netIncomingMessage = this.client.ReadMessage()) != null) + { + switch (netIncomingMessage.MessageType) + { + case NetIncomingMessageType.StatusChanged: + this.statusChanged(netIncomingMessage); + continue; + case NetIncomingMessageType.Data: + this.parseDataMessageFromServer(netIncomingMessage); + continue; + case NetIncomingMessageType.DiscoveryResponse: + Console.WriteLine("Found server at " + (object)netIncomingMessage.SenderEndPoint); + if (this.validateProtocol(netIncomingMessage.ReadString())) + { + this.serverName = netIncomingMessage.ReadString(); + this.receiveHandshake(netIncomingMessage); + this.serverDiscovered = true; + continue; + } + this.connectionMessage = "Strings\\UI:CoopMenu_FailedProtocolVersion"; + this.client.Disconnect(""); + continue; + case NetIncomingMessageType.DebugMessage: + case NetIncomingMessageType.WarningMessage: + case NetIncomingMessageType.ErrorMessage: + string str = netIncomingMessage.ReadString(); + Console.WriteLine("{0}: {1}", (object)netIncomingMessage.MessageType, (object)str); + Game1.debugOutput = str; + continue; + default: + continue; + } + } + if (this.client.ServerConnection == null || DateTime.Now.Second % 2 != 0) + return; + Game1.debugOutput = "Ping: " + (object)(float)((double)this.client.ServerConnection.AverageRoundtripTime * 1000.0) + "ms"; + } + + private void receiveHandshake(NetIncomingMessage msg) + { + this.client.Connect(msg.SenderEndPoint.Address.ToString(), msg.SenderEndPoint.Port); + } + + private void statusChanged(NetIncomingMessage message) + { + switch ((NetConnectionStatus)message.ReadByte()) + { + case NetConnectionStatus.Disconnecting: + case NetConnectionStatus.Disconnected: + this.clientRemotelyDisconnected(); + break; + } + } + + private void clientRemotelyDisconnected() + { + this.timedOut = true; + } + + public override void sendMessage(OutgoingMessage message) + { + NetOutgoingMessage message1 = this.client.CreateMessage(); + using (NetBufferWriteStream bufferWriteStream = new NetBufferWriteStream((NetBuffer)message1)) + { + using (BinaryWriter writer = new BinaryWriter((Stream)bufferWriteStream)) + message.Write(writer); + } + int num = (int)this.client.SendMessage(message1, NetDeliveryMethod.ReliableOrdered); + } + + private void parseDataMessageFromServer(NetIncomingMessage dataMsg) + { + using (IncomingMessage message = new IncomingMessage()) + { + using (NetBufferReadStream bufferReadStream = new NetBufferReadStream((NetBuffer)dataMsg)) + { + using (BinaryReader reader = new BinaryReader((Stream)bufferReadStream)) + { + while ((long)dataMsg.LengthBits - dataMsg.Position >= 8L) + { + message.Read(reader); + try + { + this.processIncomingMessage(message); + } + catch(Exception err) + { + + } + } + } + } + } + } + } +} diff --git a/GeneralMods/StardustCore/NetCode/ModdedGameServer.cs b/GeneralMods/StardustCore/NetCode/ModdedGameServer.cs new file mode 100644 index 00000000..6868e7fd --- /dev/null +++ b/GeneralMods/StardustCore/NetCode/ModdedGameServer.cs @@ -0,0 +1,424 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Serialization; +using Microsoft.Xna.Framework; +using Netcode; +using StardewValley; +using StardewValley.Buildings; +using StardewValley.Locations; +using StardewValley.Minigames; +using StardewValley.Network; + +namespace StardustCore.NetCode +{ + public class GameServer : IGameServer + { + public List servers = new List(); + public List pendingGameAvailableActions = new List(); + + public GameServer() + { + this.servers.Add(ModCore.multiplayer.InitServer((Server)new LidgrenServer((IGameServer)this))); + if (Program.sdk.Networking == null) + return; + this.servers.Add(Program.sdk.Networking.CreateServer((IGameServer)this)); + } + + public int connectionsCount + { + get + { + return this.servers.Sum((Func)(s => s.connectionsCount)); + } + } + + public string getInviteCode() + { + foreach (Server server in this.servers) + { + string inviteCode = server.getInviteCode(); + if (inviteCode != null) + return inviteCode; + } + return (string)null; + } + + public string getUserName(long farmerId) + { + foreach (Server server in this.servers) + { + if (server.getUserName(farmerId) != null) + return server.getUserName(farmerId); + } + return (string)null; + } + + protected void initialize() + { + foreach (Server server in this.servers) + server.initialize(); + this.updateLobbyData(); + } + + public void setPrivacy(ServerPrivacy privacy) + { + foreach (Server server in this.servers) + server.setPrivacy(privacy); + } + + public void stopServer() + { + foreach (Server server in this.servers) + server.stopServer(); + } + + public void receiveMessages() + { + foreach (Server server in this.servers) + { + if (server == null) + { + ModCore.ModMonitor.Log("ERROR"); + continue; + } + server.receiveMessages(); + } + if (!this.isGameAvailable()) + return; + foreach (Action gameAvailableAction in this.pendingGameAvailableActions) + gameAvailableAction(); + this.pendingGameAvailableActions.Clear(); + } + + public void sendMessage(long peerId, OutgoingMessage message) + { + foreach (Server server in this.servers) + server.sendMessage(peerId, message); + } + + public bool canAcceptIPConnections() + { + return this.servers.Select((Func)(s => s.canAcceptIPConnections())).Aggregate(false, (Func)((a, b) => a | b)); + } + + public bool canOfferInvite() + { + return this.servers.Select((Func)(s => s.canOfferInvite())).Aggregate(false, (Func)((a, b) => a | b)); + } + + public void offerInvite() + { + foreach (Server server in this.servers) + { + if (server.canOfferInvite()) + server.offerInvite(); + } + } + + public bool connected() + { + foreach (Server server in this.servers) + { + if (!server.connected()) + return false; + } + return true; + } + + public void sendMessage(long peerId, byte messageType, Farmer sourceFarmer, params object[] data) + { + this.sendMessage(peerId, new OutgoingMessage(messageType, sourceFarmer, data)); + } + + public void sendMessages() + { + foreach (Farmer farmer in (IEnumerable)Game1.otherFarmers.Values) + { + foreach (OutgoingMessage message in (IEnumerable)farmer.messageQueue) + this.sendMessage(farmer.UniqueMultiplayerID, message); + farmer.messageQueue.Clear(); + } + } + + public void startServer() + { + Console.WriteLine("Starting server. Protocol version: " + ModCore.multiplayer.protocolVersion); + this.initialize(); + if ((NetFieldBase>)Game1.serverHost == (NetRef)null) + Game1.serverHost = new NetFarmerRoot(); + Game1.serverHost.Value = Game1.player; + Game1.serverHost.MarkClean(); + Game1.serverHost.Clock.InterpolationTicks = ModCore.multiplayer.defaultInterpolationTicks; + if ((NetFieldBase>)Game1.netWorldState == (NetRef)null) + Game1.netWorldState = new NetRoot((IWorldState)new NetWorldState()); + Game1.netWorldState.Clock.InterpolationTicks = 0; + Game1.netWorldState.Value.UpdateFromGame1(); + } + + public void sendServerIntroduction(long peer) + { + this.sendLocation(peer, (GameLocation)Game1.getFarm()); + this.sendLocation(peer, Game1.getLocationFromName("FarmHouse")); + ModCore.SerializationManager.cleanUpInventory(); + ModCore.SerializationManager.cleanUpWorld(); + ModCore.SerializationManager.cleanUpStorageContainers(); + ModCore.ModMonitor.Log("HELLO LOVE!!!"); + this.sendMessage(peer, new OutgoingMessage((byte)1, Game1.serverHost.Value, new object[3] + { + (object)ModCore.multiplayer.writeObjectFullBytes((NetRoot)Game1.serverHost, new long?(peer)), + (object)ModCore.multiplayer.writeObjectFullBytes(Game1.player.teamRoot, new long?(peer)), + (object)ModCore.multiplayer.writeObjectFullBytes(Game1.netWorldState, new long?(peer)) + })); + foreach (KeyValuePair> root in Game1.otherFarmers.Roots) + { + if (root.Key != Game1.player.UniqueMultiplayerID && root.Key != peer) + this.sendMessage(peer, new OutgoingMessage((byte)2, root.Value.Value, new object[2] + { + (object)this.getUserName(root.Value.Value.UniqueMultiplayerID), + (object)ModCore.multiplayer.writeObjectFullBytes(root.Value, new long?(peer)) + })); + } + ModCore.SerializationManager.restoreAllModObjects(ModCore.SerializationManager.trackedObjectList); + } + + public void playerDisconnected(long disconnectee) + { + Farmer sourceFarmer = (Farmer)null; + Game1.otherFarmers.TryGetValue(disconnectee, out sourceFarmer); + ModCore.multiplayer.playerDisconnected(disconnectee); + if (sourceFarmer == null) + return; + OutgoingMessage message = new OutgoingMessage((byte)19, sourceFarmer, new object[0]); + foreach (long key in (IEnumerable)Game1.otherFarmers.Keys) + { + if (key != disconnectee) + this.sendMessage(key, message); + } + } + + public bool isGameAvailable() + { + bool flag1 = Game1.currentMinigame is Intro || Game1.Date.DayOfMonth == 0; + bool flag2 = Game1.CurrentEvent != null && Game1.CurrentEvent.isWedding; + bool flag3 = Game1.newDaySync != null && !Game1.newDaySync.hasFinished(); + bool flag4 = Game1.player.team.buildingLock.IsLocked(); + if (!Game1.isFestival() && !flag2 && (!flag1 && !flag3)) + return !flag4; + return false; + } + + public bool whenGameAvailable(Action action) + { + if (this.isGameAvailable()) + { + action(); + return true; + } + this.pendingGameAvailableActions.Add(action); + return false; + } + + private void rejectFarmhandRequest(string userID, NetFarmerRoot farmer, Action sendMessage) + { + this.sendAvailableFarmhands(userID, sendMessage); + Console.WriteLine("Rejected request for farmhand " + (farmer.Value != null ? farmer.Value.UniqueMultiplayerID.ToString() : "???")); + } + + private IEnumerable cabins() + { + if (Game1.getFarm() != null) + { + foreach (Building building in Game1.getFarm().buildings) + { + if ((int)((NetFieldBase)building.daysOfConstructionLeft) <= 0 && building.indoors.Value is Cabin) + yield return building.indoors.Value as Cabin; + } + } + } + + private bool authCheck(string userID, Farmer farmhand) + { + if (!Game1.options.enableFarmhandCreation && !(bool)((NetFieldBase)farmhand.isCustomized)) + return false; + if (!(userID == "") && !(farmhand.userID.Value == "")) + return farmhand.userID.Value == userID; + return true; + } + + private Cabin findCabin(Farmer farmhand) + { + foreach (Cabin cabin in this.cabins()) + { + if (cabin.getFarmhand().Value.UniqueMultiplayerID == farmhand.UniqueMultiplayerID) + return cabin; + } + return (Cabin)null; + } + + private Farmer findOriginalFarmhand(Farmer farmhand) + { + return this.findCabin(farmhand)?.getFarmhand().Value; + } + + public void checkFarmhandRequest(string userID, NetFarmerRoot farmer, Action sendMessage, Action approve) + { + if (farmer.Value == null) + { + this.rejectFarmhandRequest(userID, farmer, sendMessage); + } + else + { + long id = farmer.Value.UniqueMultiplayerID; + if (this.whenGameAvailable((Action)(() => + { + Farmer originalFarmhand = this.findOriginalFarmhand(farmer.Value); + if (originalFarmhand == null) + { + Console.WriteLine("Rejected request for farmhand " + (object)id + ": doesn't exist"); + this.rejectFarmhandRequest(userID, farmer, sendMessage); + } + else if (!this.authCheck(userID, originalFarmhand)) + { + Console.WriteLine("Rejected request for farmhand " + (object)id + ": authorization failure"); + this.rejectFarmhandRequest(userID, farmer, sendMessage); + } + else if (Game1.otherFarmers.ContainsKey(id) && !ModCore.multiplayer.isDisconnecting(id) || Game1.serverHost.Value.UniqueMultiplayerID == id) + { + Console.WriteLine("Rejected request for farmhand " + (object)id + ": already in use"); + this.rejectFarmhandRequest(userID, farmer, sendMessage); + } + else if (this.findCabin(farmer.Value).isInventoryOpen()) + { + Console.WriteLine("Rejected request for farmhand " + (object)id + ": inventory in use"); + this.rejectFarmhandRequest(userID, farmer, sendMessage); + } + else + { + Console.WriteLine("Approved request for farmhand " + (object)id); + approve(); + ModCore.multiplayer.addPlayer(farmer); + ModCore.multiplayer.broadcastPlayerIntroduction(farmer); + this.sendServerIntroduction(id); + this.updateLobbyData(); + } + }))) + return; + Console.WriteLine("Postponing request for farmhand " + (object)id); + sendMessage(new OutgoingMessage((byte)11, Game1.player, new object[1] + { + (object)"Strings\\UI:Client_WaitForHostAvailability" + })); + } + } + + public void sendAvailableFarmhands(string userID, Action sendMessage) + { + List> netRefList = new List>(); + Game1.getFarm(); + foreach (Cabin cabin in this.cabins()) + { + NetRef farmhand = cabin.getFarmhand(); + if ((!farmhand.Value.isActive() || ModCore.multiplayer.isDisconnecting(farmhand.Value.UniqueMultiplayerID)) && (this.authCheck(userID, farmhand.Value) && !cabin.isInventoryOpen())) + netRefList.Add(farmhand); + } + using (MemoryStream memoryStream = new MemoryStream()) + { + using (BinaryWriter writer = new BinaryWriter((Stream)memoryStream)) + { + writer.Write(Game1.year); + writer.Write(Utility.getSeasonNumber(Game1.currentSeason)); + writer.Write(Game1.dayOfMonth); + writer.Write((byte)netRefList.Count); + foreach (NetRef netRef in netRefList) + { + try + { + netRef.Serializer = SaveGame.farmerSerializer; + netRef.WriteFull(writer); + } + finally + { + netRef.Serializer = (XmlSerializer)null; + } + } + memoryStream.Seek(0L, SeekOrigin.Begin); + sendMessage(new OutgoingMessage((byte)9, Game1.player, new object[1] + { + (object)memoryStream.ToArray() + })); + } + } + } + + private void sendLocation(long peer, GameLocation location) + { + this.sendMessage(peer, (byte)3, Game1.serverHost.Value, (object)ModCore.multiplayer.writeObjectFullBytes(ModCore.multiplayer.locationRoot(location), new long?(peer))); + } + + private void warpFarmer(Farmer farmer, short x, short y, string name, bool isStructure) + { + GameLocation locationFromName = Game1.getLocationFromName(name, isStructure); + locationFromName.hostSetup(); + farmer.currentLocation = locationFromName; + farmer.Position = new Vector2((float)((int)x * 64), (float)((int)y * 64 - (farmer.Sprite.getHeight() - 32) + 16)); + this.sendLocation(farmer.UniqueMultiplayerID, locationFromName); + } + + public void processIncomingMessage(IncomingMessage message) + { + switch (message.MessageType) + { + case 2: + message.Reader.ReadString(); + ModCore.multiplayer.processIncomingMessage(message); + break; + case 5: + this.warpFarmer(message.SourceFarmer, message.Reader.ReadInt16(), message.Reader.ReadInt16(), message.Reader.ReadString(), message.Reader.ReadByte() == (byte)1); + break; + default: + ModCore.multiplayer.processIncomingMessage(message); + break; + } + if (!ModCore.multiplayer.isClientBroadcastType(message.MessageType)) + return; + this.rebroadcastClientMessage(message); + } + + private void rebroadcastClientMessage(IncomingMessage message) + { + OutgoingMessage message1 = new OutgoingMessage(message); + foreach (long key in (IEnumerable)Game1.otherFarmers.Keys) + { + if (key != message.FarmerID) + this.sendMessage(key, message1); + } + } + + private void setLobbyData(string key, string value) + { + foreach (Server server in this.servers) + server.setLobbyData(key, value); + } + + private bool unclaimedFarmhandsExist() + { + foreach (Cabin cabin in this.cabins()) + { + if (cabin.farmhand.Value == null || cabin.farmhand.Value.userID.Value == "") + return true; + } + return false; + } + + public void updateLobbyData() + { + this.setLobbyData("farmName", Game1.player.farmName.Value); + this.setLobbyData("farmType", Convert.ToString(Game1.whichFarm)); + this.setLobbyData("date", Convert.ToString(new WorldDate(Game1.year, Game1.currentSeason, Game1.dayOfMonth).TotalDays)); + this.setLobbyData("farmhands", string.Join(",", Game1.getAllFarmhands().Select((Func)(farmhand => farmhand.userID.Value)).Where((Func)(user => user != "")))); + this.setLobbyData("newFarmhands", Convert.ToString(Game1.options.enableFarmhandCreation && this.unclaimedFarmhandsExist())); + } + } +} diff --git a/GeneralMods/StardustCore/NetCode/NetBufferReadStream.cs b/GeneralMods/StardustCore/NetCode/NetBufferReadStream.cs new file mode 100644 index 00000000..0b24e59a --- /dev/null +++ b/GeneralMods/StardustCore/NetCode/NetBufferReadStream.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using Lidgren.Network; + +namespace StardustCore.NetCode +{ + public class NetBufferReadStream : Stream + { + private long offset; + public NetBuffer Buffer; + + public NetBufferReadStream(NetBuffer buffer) + { + this.Buffer = buffer; + this.offset = buffer.Position; + } + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanSeek + { + get + { + return true; + } + } + + public override bool CanWrite + { + get + { + return false; + } + } + + public override long Length + { + get + { + return ((long)this.Buffer.LengthBits - this.offset) / 8L; + } + } + + public override long Position + { + get + { + return (this.Buffer.Position - this.offset) / 8L; + } + set + { + this.Buffer.Position = this.offset + value * 8L; + } + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + this.Buffer.ReadBytes(buffer, offset, count); + return count; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + this.Position = offset; + break; + case SeekOrigin.Current: + this.Position += offset; + break; + case SeekOrigin.End: + this.Position = this.Length + offset; + break; + } + return this.Position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} diff --git a/GeneralMods/StardustCore/NetCode/NetBufferWriteStream.cs b/GeneralMods/StardustCore/NetCode/NetBufferWriteStream.cs new file mode 100644 index 00000000..e3b62d41 --- /dev/null +++ b/GeneralMods/StardustCore/NetCode/NetBufferWriteStream.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using Lidgren.Network; + +namespace StardustCore.NetCode +{ + public class NetBufferWriteStream : Stream + { + private int offset; + public NetBuffer Buffer; + + public NetBufferWriteStream(NetBuffer buffer) + { + this.Buffer = buffer; + this.offset = buffer.LengthBits; + } + + public override bool CanRead + { + get + { + return false; + } + } + + public override bool CanSeek + { + get + { + return true; + } + } + + public override bool CanWrite + { + get + { + return true; + } + } + + public override long Length + { + get + { + throw new NotSupportedException(); + } + } + + public override long Position + { + get + { + return (long)((this.Buffer.LengthBits - this.offset) / 8); + } + set + { + this.Buffer.LengthBits = (int)((long)this.offset + value * 8L); + } + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + this.Position = offset; + break; + case SeekOrigin.Current: + this.Position += offset; + break; + case SeekOrigin.End: + throw new NotSupportedException(); + } + return this.Position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + this.Buffer.Write(buffer, offset, count); + } + } +} diff --git a/GeneralMods/StardustCore/NetCode/NetCoreObject.cs b/GeneralMods/StardustCore/NetCode/NetCoreObject.cs index 653102fe..47bfdf7f 100644 --- a/GeneralMods/StardustCore/NetCode/NetCoreObject.cs +++ b/GeneralMods/StardustCore/NetCode/NetCoreObject.cs @@ -63,7 +63,7 @@ namespace StardustCore.NetCode { texture = new NetTexture2DExtended(Value.getExtendedTexture()); - texture.WriteFull(writer); + texture.Write(writer); which = new NetInt(Value.ParentSheetIndex); which.Write(writer); diff --git a/GeneralMods/StardustCore/NetCode/NetTexure2DExtended.cs b/GeneralMods/StardustCore/NetCode/NetTexure2DExtended.cs index 47d8cfe6..4b3905f9 100644 --- a/GeneralMods/StardustCore/NetCode/NetTexure2DExtended.cs +++ b/GeneralMods/StardustCore/NetCode/NetTexure2DExtended.cs @@ -38,86 +38,20 @@ namespace StardustCore.NetCode protected override void ReadDelta(BinaryReader reader, NetVersion version) { - - NetInt Width = new NetInt(); - Width.Read(reader,version); - int width = Width.Value; - - NetInt Height = new NetInt(); - Height.Read(reader, version); - int height = Height.Value; - - NetString name = new NetString(); name.Read(reader, version); - NetString path = new NetString(); - path.Read(reader, version); - - - NetInt count = new NetInt(); - count.Read(reader, version); - - List bytes = new List(); - //colorsOne = reader.ReadBytes(); - - for(int i=0; i < count.Value; i++) - { - NetColor col = new NetColor(); - col.Read(reader, version); - bytes.Add(col.Value); - } - - ModCore.ModMonitor.Log("Finished length: "+bytes.Count.ToString()); - ModCore.ModMonitor.Log("W: " + width.ToString()); - ModCore.ModMonitor.Log("H: " + height.ToString()); //Texture2D texture = new Texture2D(Game1.graphics.GraphicsDevice,width,height); - Texture2DExtended texture = new Texture2DExtended(ModCore.ModHelper, Path.Combine("Content", "Graphics", "MultiTest", "Test1.png")); + Texture2DExtended texture = ModCore.TextureManager.getTexture(name.Value); this.Value = texture; - - //texture.SetData(bytes.ToArray()); - - //Value.Name = name.Value; - //Value.path = path.Value; - //Value.setTexure(texture); } protected override void WriteDelta(BinaryWriter writer) { - - int size = Value.getTexture().Width * Value.getTexture().Height; - NetInt Width = new NetInt(Value.getTexture().Width); - Width.Write(writer); - - NetInt Height = new NetInt(Value.getTexture().Height); - Height.Write(writer); - - NetString name = new NetString(Value.Name); name.Write(writer); - - NetString path = new NetString(Value.path); - path.Write(writer); - //writer.Write(size); - - Texture2D texture = Value.getTexture(); - Color[] colorsOne = new Color[size]; //The hard to read,1D array - - texture.GetData(colorsOne); - - NetInt count = new NetInt(colorsOne.Length); - count.Write(writer); - - ModCore.ModMonitor.Log("Color length:" + count.ToString()); - - foreach(var v in colorsOne) - { - NetColor col = new NetColor(v); - col.Write(writer); - } - } diff --git a/GeneralMods/StardustCore/Objects/CoreObject.cs b/GeneralMods/StardustCore/Objects/CoreObject.cs index f83f6d6f..19abd983 100644 --- a/GeneralMods/StardustCore/Objects/CoreObject.cs +++ b/GeneralMods/StardustCore/Objects/CoreObject.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Netcode; +using Newtonsoft.Json; using StardewModdingAPI; using StardewValley; using StardewValley.Locations; @@ -11,6 +12,7 @@ using StardustCore.Interfaces; using StardustCore.UIUtilities; using System; using System.Collections.Generic; +using System.IO; using System.Xml.Serialization; namespace StardustCore @@ -42,7 +44,7 @@ namespace StardustCore public string description; - [XmlIgnore] + protected Texture2DExtended TextureSheet; public new bool flipped; @@ -80,6 +82,7 @@ namespace StardustCore public string serializationName; + public string textureName; public override string Name { @@ -130,10 +133,6 @@ namespace StardustCore public CoreObject(Texture2DExtended texture,int which, Vector2 Tile, int InventoryMaxSize):base() { var ok=Game1.getAllFarmers(); - foreach (var v in ok){ - this.owner.Value = v.UniqueMultiplayerID; - break; - } this.Type = "Not Sure"; this.CanBeSetDown = true; this.CanBeGrabbed = true; @@ -1223,6 +1222,66 @@ namespace StardustCore { this.TextureSheet = texture; } + + + /// + /// Serializes the said item properly. + /// + /// + public static void Serialize(Item I) + { + String savePath = ModCore.SerializationManager.playerInventoryPath; + String fileName = I.Name + ".json"; + String resultPath = Path.Combine(savePath, fileName); + int count = 0; + while (File.Exists(resultPath)) + { + resultPath = Serialization.SerializationManager.getValidSavePathIfDuplicatesExist(I, savePath, count); + count++; + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + (I as CoreObject).textureName = (I as CoreObject).TextureSheet.Name; + string json = JsonConvert.SerializeObject(I, Formatting.Indented,settings); + System.IO.File.WriteAllText(resultPath, json); + //StardustCore.ModCore.ModHelper.WriteJsonFile(resultPath, (CoreObject)I); + } + + /// + /// Serializes the said item to a chest. + /// + /// + /// + public static void SerializeToContainer(Item I, string s) + { + String savePath = s; + String fileName = I.Name + ".json"; + String resultPath = Path.Combine(savePath, fileName); + int count = 0; + while (File.Exists(resultPath)) + { + resultPath = Serialization.SerializationManager.getValidSavePathIfDuplicatesExist(I, savePath, count); + count++; + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + (I as CoreObject).textureName = (I as CoreObject).TextureSheet.Name; + string json = JsonConvert.SerializeObject(I, Formatting.Indented, settings); + System.IO.File.WriteAllText(resultPath, json); + //StardustCore.ModCore.ModHelper.WriteJsonFile(resultPath, (CoreObject)I); + } + + /// + /// Deserializes the object from a .json. + /// + /// + /// + public static CoreObject Deserialize(string data) + { + CoreObject obj = ModCore.ModHelper.ReadJsonFile(data); + obj.TextureSheet = ModCore.TextureManager.getTexture(obj.textureName); + return obj; + } } } diff --git a/GeneralMods/StardustCore/Serialization/Serialization.cs b/GeneralMods/StardustCore/Serialization/Serialization.cs index f59e351b..1b858e0b 100644 --- a/GeneralMods/StardustCore/Serialization/Serialization.cs +++ b/GeneralMods/StardustCore/Serialization/Serialization.cs @@ -842,6 +842,12 @@ public string ParseXMLType(string path) public void initializeDefaultSuportedTypes() { initializeSupportedToolTypes(); + initializeSupportedObjectTypes(); + } + + private void initializeSupportedObjectTypes() + { + this.acceptedTypes.Add(typeof(CoreObject).ToString(), new SerializerDataNode(CoreObject.Serialize, CoreObject.Deserialize, new SerializerDataNode.WorldParsingFunction(CoreObject.Serialize), new SerializerDataNode.SerializingToContainerFunction(CoreObject.SerializeToContainer))); } /// diff --git a/GeneralMods/StardustCore/StardustCore.csproj b/GeneralMods/StardustCore/StardustCore.csproj index fd8c09bd..e07c8a51 100644 --- a/GeneralMods/StardustCore/StardustCore.csproj +++ b/GeneralMods/StardustCore/StardustCore.csproj @@ -71,6 +71,10 @@ MinimumRecommendedRules.ruleset + + False + ..\..\..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Lidgren.Network.dll + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll @@ -89,6 +93,10 @@ + + + + diff --git a/GeneralMods/StardustCore/UIUtilities/Texture2DExtended.cs b/GeneralMods/StardustCore/UIUtilities/Texture2DExtended.cs index 49de1b8b..08a31641 100644 --- a/GeneralMods/StardustCore/UIUtilities/Texture2DExtended.cs +++ b/GeneralMods/StardustCore/UIUtilities/Texture2DExtended.cs @@ -12,7 +12,7 @@ namespace StardustCore.UIUtilities public class Texture2DExtended { public string Name; - private Texture2D texture; + public Texture2D texture; public string path; IModHelper helper; diff --git a/GeneralMods/StardustCore/UIUtilities/TextureManager.cs b/GeneralMods/StardustCore/UIUtilities/TextureManager.cs index c4c0402a..33071a4d 100644 --- a/GeneralMods/StardustCore/UIUtilities/TextureManager.cs +++ b/GeneralMods/StardustCore/UIUtilities/TextureManager.cs @@ -29,7 +29,7 @@ namespace StardustCore.UIUtilities { foreach(var v in textures) { - if (v.Key == name) return v.Value; + if (v.Key == name) return v.Value.Copy(); } return null; }