diff --git a/.gitignore b/.gitignore index 0e72d7b..56be967 100644 --- a/.gitignore +++ b/.gitignore @@ -33,28 +33,13 @@ !Thunderstore LC_API.dll -## Projects +## Solution !LC-API.sln -### LC-API +### LC-API Project !LC-API/ LC-API/[Bb]in/ LC-API/[Oo]bj/ -### Source Modules -!BundleAPI -!ClientAPI -!Comp -!Data -!Exceptions -!Extensions -!GameInterfaceAPI -!ManualPatches -!ServerAPI -!Networking - -### Source Files -!**.cs - # Explicit exceptions/ignores **.user \ No newline at end of file diff --git a/Thunderstore/README.md b/CHANGELOG.md similarity index 73% rename from Thunderstore/README.md rename to CHANGELOG.md index 0608250..d23c848 100644 --- a/Thunderstore/README.md +++ b/CHANGELOG.md @@ -1,108 +1,110 @@ -# LC-API -The definitive Lethal Company modding API. Includes some very useful features to make modding life easier. +# Changelog -# For Developers -If you want to use the API in your plugin, add the LC_API.dll as a project reference! +All notable changes to this project will be documented in this file. -# Features -AssetBundle loading - Put asset bundles in BepInEx > Bundles and load them using BundleAPI.BundleLoader.GetLoadedAsset +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -ServerAPI - Utilities relating to the network and server. This includes: +## [Unreleased] -ModdedServer - Automatically alerts other users when you host a server that your server is modded. -It also lets mod authors make their mods put users in special matchmaking where they can only play with other modded users +## Version [3.2.0] -Networking - Easily send data across the network to sync data between clients +- Added `Networking` namespace + - Provides much better networking that the previous `ServerAPI.Networking` class, which still exists for backwards compatibility. + - See the [wiki](https://github.com/u-2018/LC-API/wiki/Networking) for usage instructions. +- Added CI/CD github actions. +- Changed a hard-coded file location to be dynamically based off of where the plugin file is to prevent an issue with manual installation. +- Significantly revamped project structure. -# Releases +## Version [3.1.0] -# Version 1.0.0 -- Release +- Added `Item` class for interacting with grabbable objects easily. +- The `Player` class now has multiple new properties for inventory management. + - `Player.Inventory` will return a `PlayerInventory` for this. +- The `Player.Joined` event should now work properly on LAN. -# Version 1.1.0 -- General bug fixes for Networking +## Version [3.0.2] -- The local player now will NOT receive data that they broadcast. The bool value on the receive delegates is also gone. If you were using Networking, you will need to ajust your code. +- Fixed the command handler "eating" messages if they started with the command prefix, but weren't registered as commands. -# Version 1.1.1 -- General bug fixes for Networking +## Version [3.0.1] -- Plugin developers NEED to update to this version as it includes a fix for a bug that prevented Networking from being used. +- Fixed `Player.HostPlayer`. -# Version 1.2.0 -- Added new GameInterfaceAPI. Documentation will be created soon. +## Version [3.0.0] -- Added new CheatDatabase with the purpose of catching users trying to join non-modded servers with cheaty mods. The CheatDatabase also allows for the host to view all mods installed by people joining. (As long as they have LC_API installed). +- Removed automated bundle loading. + - Legacy loading will still automatically load bundles if wanted. +- Added event system. + - More events to be added in future. +- Added `Player` class for interacting with players easily. +- `ModdedServer.GameVersion` will now contain the base game version even if LC API modified the version to set modded only. -# Version 1.2.1 -- Adjusted README formatting. +## Version [2.2.0] -# Version 1.3.0 -- Changed how the BundleLoader in the BundleAPI loads assets to fix issues caused by downloading mods from mod managers. The path BepInEx > Bundles is outdated and should not be used anymore. +- Added a command handler. +- The bundle loader will only attempt to load actual bundles. +- Added a temporary fix for lethal expansion bundles. Will be looking into a long term solution in the next update. +- The local player now will NOT receive data that they broadcast. The bool value on the receive delegates is also gone. If you were using Networking, you will need to ajust your code. -# Version 1.4.0 -- Changed how the BundleLoader in the BundleAPI loads assets to fix issues with certain languages. This will break some mods, but a config option is included to revert to the old system so you can still use older mods. +## Version [2.1.2] -- If you are a plugin developer, use GetLoadedAsset to get an asset, instead of using the asset dictionary. This ensures that your plugin will still work even when changes like this are made. +- Updated to game version 45. -# Version 1.4.1 -- LC_API should now be able to load no matter if the Hide Manager GameObject option is on or off. +## Version [2.1.1] -- A config option has been added that will disable the BundleLoader. +- Actually fixed the BundleLodaer loading assets twice. +- Added new CheatDatabase with the purpose of catching users trying to join non-modded servers with cheaty mods. The CheatDatabase also allows for the host to view all mods installed by people joining. (As long as they have LC_API installed). -# Version 1.4.2 -- Fix for the new config option causing the API to fail to initialize. +## Version [2.1.0] -# Version 2.0.0 -- Changes to the BundleLoader to stop conflicts with other plugins loading assets without the BundleLoader. +- Fixed the BundleLodaer loading assets twice. -- Changes to Networking and GameState events, plugins using these will need to be rebuilt. +## Version [2.0.0] +- Changes to the BundleLoader to stop conflicts with other plugins loading assets without the BundleLoader. +- Changes to Networking and GameState events, plugins using these will need to be rebuilt. - Added GameTips to GameInterfaceAPI. GameTips uses a tip que system to ensure no popup tip messages overlap with eachother. - - Changed the CheatDatabase to now (in theory) work for all players, not just the host. - - Changes the CheatDatabase to use GameTips to display information. It will still output information to the logs. -# Version 2.1.0 -- Fixed the BundleLodaer loading assets twice. +## Version [1.4.2] +- Fix for the new config option causing the API to fail to initialize. -# Version 2.1.1 -- Actually fixed the BundleLodaer loading assets twice. -# Version 2.1.2 -- Updated to game version 45. +## Version [1.4.1] -# Version 2.2.0 -- Added a command handler. +- LC_API should now be able to load no matter if the Hide Manager GameObject option is on or off. +- A config option has been added that will disable the BundleLoader. -- The bundle loader will only attempt to load actual bundles. -- Added a temporary fix for lethal expansion bundles. Will be looking into a long term solution in the next update. +## Version [1.4.0] -# Version 3.0.0 -- Removed automated bundle loading. - - Legacy loading will still automatically load bundles if wanted. +- Changed how the BundleLoader in the BundleAPI loads assets to fix issues with certain languages. This will break some mods, but a config option is included to revert to the old system so you can still use older mods. +- If you are a plugin developer, use GetLoadedAsset to get an asset, instead of using the asset dictionary. This ensures that your plugin will still work even when changes like this are made. -- Added event system. - - More events to be added in future. -- Added `Player` class for interacting with players easily. +## Version [1.3.0] -- `ModdedServer.GameVersion` will now contain the base game version even if LC API modified the version to set modded only. +- Changed how the BundleLoader in the BundleAPI loads assets to fix issues caused by downloading mods from mod managers. The path BepInEx > Bundles is outdated and should not be used anymore. -# Version 3.0.1 -- Fixed `Player.HostPlayer`. +## Version [1.2.1] -# Version 3.0.2 -- Fixed the command handler "eating" messages if they started with the command prefix, but weren't registered as commands. +- Adjusted README formatting. -# Version 3.1.0 -- Added `Item` class for interacting with grabbable objects easily. -- The `Player` class now has multiple new properties for inventory management. - - `Player.Inventory` will return a `PlayerInventory` for this. -- The `Player.Joined` event should now work properly on LAN. -# Version 3.2.0 -- Added `Networking` namespace - - Provides much better networking that the previous `ServerAPI.Networking` class, which still exists for backwards compatibility \ No newline at end of file +## Version [1.2.0] + +- Added new GameInterfaceAPI. Documentation will be created soon. + +## Version [1.1.1] + +- General bug fixes for Networking + +## Version [1.1.0] + +- General bug fixes for Networking + +## Version [1.0.0] + +- Release diff --git a/LC-API/LC-API.csproj b/LC-API/LC-API.csproj index 39b7ad4..b4fec9c 100644 --- a/LC-API/LC-API.csproj +++ b/LC-API/LC-API.csproj @@ -35,12 +35,6 @@ - - - - - - diff --git a/LC-API/Networking/Network.cs b/LC-API/Networking/Network.cs index b7ac52b..4bdc43d 100644 --- a/LC-API/Networking/Network.cs +++ b/LC-API/Networking/Network.cs @@ -11,6 +11,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -52,99 +53,142 @@ internal static T ToObject(this byte[] bytes) where T : class internal static void OnUnregisterNetworkMessages() => UnregisterNetworkMessages.InvokeSafely(); + private static MethodInfo _registerInfo = null; + private static MethodInfo _registerInfoGeneric = null; + + internal static MethodInfo RegisterInfo + { + get + { + if (_registerInfo == null) + { + foreach (MethodInfo methodInfo in typeof(Network).GetMethods()) + { + if (methodInfo.Name == nameof(RegisterMessage) && !methodInfo.IsGenericMethod) + { + _registerInfo = methodInfo; + break; + } + } + } + + return _registerInfo; + } + } + + internal static MethodInfo RegisterInfoGeneric + { + get + { + if (_registerInfo == null) + { + foreach (MethodInfo methodInfo in typeof(Network).GetMethods()) + { + if (methodInfo.Name == nameof(RegisterMessage) && methodInfo.IsGenericMethod) + { + _registerInfoGeneric = methodInfo; + break; + } + } + } + + return _registerInfoGeneric; + } + } + internal static void RegisterAllMessages() { foreach (NetworkMessageFinalizerBase handler in NetworkMessageFinalizers.Values) { NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler(handler.UniqueName, handler.Read); } + } - MethodInfo registerInfo = null; - MethodInfo registerInfoGeneric = null; + internal static void UnregisterAllMessages() + { - foreach (MethodInfo methodInfo in typeof(Network).GetMethods()) + foreach (string name in NetworkMessageFinalizers.Keys.ToArray()) { - if (methodInfo.Name == nameof(RegisterMessage) && methodInfo.IsGenericMethod) - { - registerInfoGeneric = methodInfo; - } - else if (methodInfo.Name == nameof(RegisterMessage) && !methodInfo.IsGenericMethod) - { - registerInfo = methodInfo; - } + UnregisterMessage(name); + } + } - if (registerInfo != null && registerInfoGeneric != null) break; + public static void RegisterAll() + { + // This cursed line of code comes from Harmony's PatchAll method. Thanks, Harmony + var m = new StackTrace().GetFrame(1).GetMethod(); + var assembly = m.ReflectedType.Assembly; + foreach (Type type in AccessTools.GetTypesFromAssembly(assembly)) + { + RegisterAll(type); } + } - foreach (PluginInfo pluginInfo in Chainloader.PluginInfos.Values) + public static void RegisterAll(Type type) + { + if (type.IsClass) { - foreach (Type type in pluginInfo.Instance.GetType().Assembly.GetTypes()) + NetworkMessage networkMessage = type.GetCustomAttribute(); + + if (networkMessage != null) { - if (type.IsClass) + if (type.BaseType.Name == "NetworkMessageHandler`1") { - NetworkMessage networkMessage = type.GetCustomAttribute(); - - if (networkMessage != null) - { - if (type.BaseType.Name == "NetworkMessageHandler`1") + Type messageType = type.BaseType.GetGenericArguments()[0]; + RegisterInfoGeneric + .MakeGenericMethod(messageType) + .Invoke(null, new object[] { - Type messageType = type.BaseType.GetGenericArguments()[0]; - registerInfoGeneric - .MakeGenericMethod(messageType) - .Invoke(null, new object[] - { networkMessage.UniqueName, networkMessage.RelayToSelf, type.GetMethod("Handler").CreateDelegate(typeof(Action<,>) .MakeGenericType(typeof(ulong), messageType), Activator.CreateInstance(type)) - }); - } - else if (type.BaseType.Name == "NetworkMessageHandler") + }); + } + else if (type.BaseType.Name == "NetworkMessageHandler") + { + RegisterInfo + .Invoke(null, new object[] { - registerInfo - .Invoke(null, new object[] - { networkMessage.UniqueName, networkMessage.RelayToSelf, type.GetMethod("Handler").CreateDelegate(typeof(Action<>) .MakeGenericType(typeof(ulong)), Activator.CreateInstance(type)) - }); - } - } - else + }); + } + } + else + { + foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic)) + { + networkMessage = method.GetCustomAttribute(); + if (networkMessage != null) { - foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic)) - { - networkMessage = method.GetCustomAttribute(); - if (networkMessage != null) - { - if (!method.IsStatic) throw new Exception("Detected NetworkMessage attribute on non-static method. All NetworkMessages on methods must be static."); + if (!method.IsStatic) throw new Exception("Detected NetworkMessage attribute on non-static method. All NetworkMessages on methods must be static."); - if (method.GetParameters().Length == 1) + if (method.GetParameters().Length == 1) + { + RegisterInfo + .Invoke(null, new object[] { - registerInfo - .Invoke(null, new object[] - { networkMessage.UniqueName, networkMessage.RelayToSelf, method.CreateDelegate(typeof(Action<>) .MakeGenericType(typeof(ulong))) - }); - } - else + }); + } + else + { + Type messageType = method.GetParameters()[1].ParameterType; + RegisterInfoGeneric + .MakeGenericMethod(messageType) + .Invoke(null, new object[] { - Type messageType = method.GetParameters()[1].ParameterType; - registerInfoGeneric - .MakeGenericMethod(messageType) - .Invoke(null, new object[] - { networkMessage.UniqueName, networkMessage.RelayToSelf, method.CreateDelegate(typeof(Action<,>) .MakeGenericType(typeof(ulong), messageType)) - }); - } - } + }); } } } @@ -152,12 +196,38 @@ internal static void RegisterAllMessages() } } - internal static void UnregisterAllMessages() + public static void UnregisterAll() { - - foreach (string name in NetworkMessageFinalizers.Keys.ToArray()) + // This cursed line of code comes from Harmony's PatchAll method. Thanks, Harmony + var m = new StackTrace().GetFrame(1).GetMethod(); + var assembly = m.ReflectedType.Assembly; + foreach (Type type in AccessTools.GetTypesFromAssembly(assembly)) { - UnregisterMessage(name); + UnregisterAll(type); + } + } + + public static void UnregisterAll(Type type) + { + if (type.IsClass) + { + NetworkMessage networkMessage = type.GetCustomAttribute(); + + if (networkMessage != null) + { + UnregisterMessage(networkMessage.UniqueName); + } + else + { + foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic)) + { + networkMessage = method.GetCustomAttribute(); + if (networkMessage != null) + { + UnregisterMessage(networkMessage.UniqueName); + } + } + } } } @@ -298,6 +368,8 @@ internal static void Init() }; SetupNetworking(); + + RegisterAll(); } internal static void SetupNetworking() diff --git a/README.md b/README.md index 1de5c57..272534d 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,13 @@ Ensure your Assembly CSharp is set `Publicize="true"` in the .csproj file to ens Once you have completed these steps, you will be able to properly build the solution. +## Making a PR +Your [pull request](https://github.com/u-2018/LC-API/compare) should target the [dev branch](https://github.com/u-2018/LC-API/tree/dev). This is because the `main` branch is reserved for tested features that are ready for release. Basically if someone were to clone the repo, they should be able to build `main` and use it without any fear of broken things. + +The `dev` branch, however, may contain untested features and is used to build release candidates. Before releases, the `dev` branch will be frozen and tested for issues, when it passes its testing then it will be merged into `main` and a release will be made. Pre-releases may come from the `dev` branch for release candidates and testing. These will be generally stable, but may still contain broken features before testing is done. + +Pull requests targeting the `main` branch will not be merged in most circumstances. Most merges to `main` will be directly from the frozen `dev` branch after testing. + # Features AssetBundle loading - Put asset bundles in BepInEx > Bundles and load them using BundleAPI.BundleLoader.GetLoadedAsset diff --git a/Thunderstore/icon.png b/Thunderstore/icon.png deleted file mode 100644 index 9950400..0000000 Binary files a/Thunderstore/icon.png and /dev/null differ diff --git a/Thunderstore/manifest.json b/Thunderstore/manifest.json deleted file mode 100644 index 4fba05b..0000000 --- a/Thunderstore/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "LC_API", - "version_number": "3.1.0", - "website_url": "https://github.com/u-2018/LC-API", - "description": "Multipurpose modding API for Lethal Company", - "dependencies": [] -} diff --git a/Thunderstore/BepinEx/plugins/Bundles/networking b/assets/bundles/networking similarity index 100% rename from Thunderstore/BepinEx/plugins/Bundles/networking rename to assets/bundles/networking diff --git a/assets/icons/icon.png b/assets/icons/icon.png index 1c15f3c..9950400 100644 Binary files a/assets/icons/icon.png and b/assets/icons/icon.png differ diff --git a/assets/thunderstore.toml b/assets/thunderstore.toml index 38373f2..44f1b18 100644 --- a/assets/thunderstore.toml +++ b/assets/thunderstore.toml @@ -21,7 +21,15 @@ outdir = "../dist" [[build.copy]] source = "../LC-API/bin/LC_API.dll" -target = "LC_API.dll" +target = "BepInEx/plugins/LC_API.dll" + +[[build.copy]] +source = "./bundles" +target = "BepInEx/plugins/Bundles/" + +[[build.copy]] +source = "../CHANGELOG.md" +target = "CHANGELOG.md" [[build.copy]] source = "../LICENSE"