Compare commits

...
Sign in to create a new pull request.

12 commits

Author SHA1 Message Date
Leath Cooper
158a43a168 Merge branch 'main' into addon-tutorial 2025-01-18 14:13:55 -05:00
Leath Cooper
d8cffe7da7 remove unnecessary Migrations directory 2025-01-05 17:21:56 -05:00
Leath Cooper
d269cb39ab Merge branch 'main' into addon-tutorial 2025-01-05 17:21:17 -05:00
Leath Cooper
2d5a7fd879 Merge branch 'main' into addon-tutorial 2025-01-01 17:17:41 -05:00
Leath Cooper
8d70809c75 update tutorial README 2025-01-01 15:16:56 -05:00
Leath Cooper
85abb0ea0a Merge branch 'main' into addon-tutorial 2025-01-01 15:07:51 -05:00
Leath Cooper
b247532419 remove unused Migrations things 2025-01-01 15:05:57 -05:00
Leath Cooper
4dc216eba7 Merge branch 'main' into addon-tutorial 2024-12-23 09:37:53 -05:00
Leath Cooper
48e0179c25 Merge branch 'main' into addon-tutorial 2024-12-18 20:13:11 -05:00
Leath Cooper
cabd707592 add to tutorial readme 2024-12-15 11:40:43 -05:00
Leath Cooper
3c3a583942 woops forgot this part 2024-12-15 11:28:23 -05:00
Leath Cooper
85aad626d5 tutorial 2024-12-15 10:54:58 -05:00
6 changed files with 233 additions and 1 deletions

View file

@ -1,3 +1,4 @@
[ [
"UOContent.dll" "UOContent.dll",
"AddonExample.dll"
] ]

View file

@ -12,6 +12,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UOContent.Tests", "Projects
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Projects\Application\Application.csproj", "{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Projects\Application\Application.csproj", "{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddonExample", "Projects\AddonExample\AddonExample.csproj", "{73F435F7-7D30-4275-8B74-D80A0BD7AC23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Projects\Logger\Logger.csproj", "{ECAD3793-A7C5-4546-AA88-77DD24574410}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Projects\Logger\Logger.csproj", "{ECAD3793-A7C5-4546-AA88-77DD24574410}"
EndProject EndProject
Global Global
@ -51,6 +53,12 @@ Global
{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.Build.0 = Release|Any CPU {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.Build.0 = Release|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Analyze|Any CPU.Build.0 = Analyze|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Release|Any CPU.Build.0 = Release|Any CPU
{ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU {ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU
{ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.Build.0 = Analyze|Any CPU {ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.Build.0 = Analyze|Any CPU
{ECAD3793-A7C5-4546-AA88-77DD24574410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ECAD3793-A7C5-4546-AA88-77DD24574410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

View file

@ -0,0 +1,32 @@
using Server.Accounting;
namespace Server.Addon;
public class Addon
{
public static void Configure()
{
// ModernUO lifecycle hook before world loads
EventSink.AccountLogin += AccountLogin;
}
public static void Initialize()
{
// ModernUO lifecycle hook after world loads
}
public static void AccountLogin(AccountLoginEventArgs e)
{
if (Accounts.GetAccount(e.Username) is not Account account)
{
return;
}
if (!account.Young)
{
return;
}
account.RemoveYoungStatus(0);
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Server.Addon</RootNamespace>
<AssemblyName>AddonExample</AssemblyName>
<Product>ModernUO Example Addon</Product>
<OutDir>..\..\Distribution\Assemblies</OutDir>
<PublishDir>..\..\Distribution\Assemblies</PublishDir>
<Configurations>Debug;Release;Analyze</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Server\Server.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage>
</ProjectReference>
<ProjectReference Include="..\UOContent\UOContent.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage>
</ProjectReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,173 @@
# Addon Assembly Tutorial
## What We'll Do
We're going to make an addon that is a separate `dll` file from the `UOContent.dll`, but has access to code from `Server` and `UOContent`.
Specifically we're going to make a small addon that removes young player status from a player when they login. This isn't a perfect example because unfortunately when an account is created it's not young yet. Just... imagine it works the first time they log in.
(note: can probably fix the shortcoming above, but this is a really basic example)
OKAY let's go...
### Create a Project
In your IDE, create a new project in the ModernUO solution. For this example we'll call it `AddonExample`. It's important that the project is created as a subdirectory of the `Projects` directory within the repository.
![001-new-project.png](001-new-project.png)
### Configure that Project
The process differs between IDEs, but we want to navigate to and open the `AddonExample.csproj` file. Replace the contents of the file with the following contents, and we will walk through it step by step.
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Server.Addon</RootNamespace>
<AssemblyName>AddonExample</AssemblyName>
<Product>ModernUO Example Addon</Product>
<OutDir>..\..\Distribution\Assemblies</OutDir>
<PublishDir>..\..\Distribution\Assemblies</PublishDir>
<Configurations>Debug;Release;Analyze</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Server\Server.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage>
</ProjectReference>
<ProjectReference Include="..\UOContent\UOContent.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage>
</ProjectReference>
</ItemGroup>
</Project>
```
#### `PropertyGroup`
* `RootNamespace` is the namespace of our addon and we will use this when we write the code.
* `AssemblyName` will determine the name of the DLL file that gets built. This is important for configuration later.
* `Product` is an arbitrary name to identify your addon.
* `OutDir` and `PublishDir` shouldn't be changed, the `UOContent.dll` file, for example, is output here, and this is where ModernUO will look for our addon.
* `Configurations` are build configurations, and probably should not change.
#### `ItemGroup` (1)
* `ProjectReference` indicates that we are referencing another project, so that we can reference code in that project in our addon. We do this twice, once for `Server` and once for `UOContent`.
#### `ItemGroup` (2)
* `AdditionalFiles` indicates we want to copy some files into the build environment. If we were to add custom items which are saved and loaded from the game we would need to provide migrations for changes in what those items save.
### Write Some Code
The project should have a directory structure something like this at this point.
```
ModernUO
Projects
AddonExample
Class1.cs
```
Remove `Class1.cs` and create a new class called `Addon`. Replace the code in that generated class with the following contents, and we'll walk through it step by step.
```csharp
using Server.Accounting;
namespace Server.Addon;
public class Addon
{
public static void Configure()
{
EventSink.AccountLogin += AccountLogin;
}
public static void Initialize()
{
}
public static void AccountLogin(AccountLoginEventArgs e)
{
if (Accounts.GetAccount(e.Username) is not Account account)
{
return;
}
if (!account.Young)
{
return;
}
account.RemoveYoungStatus(0);
}
}
```
#### The Correct Namespace
```csharp
using Server.Accounting; // ignore this for now, we will use it below
// this is the RootNamespace from AddonExample.csproj
namespace Server.Addon;
```
#### The Configure and Initialize Functions
```csharp
public static void Configure()
{
/*
The Configure function is called once before the World is loaded.
Do setup steps here.
*/
// In our case, we're going to add a callback function that will get called
// whenever the AccountLogin event is raised (magic).
EventSink.AccountLogin += AccountLogin;
}
public static void Initialize()
{
/*
We don't actually use this here, but it's important to know about the Initialize function.
After the world loads, but before the game starts, this function is called once.
Do setup steps here.
*/
}
```
#### Do Something
```csharp
// This is the function we added to the AccountLogin event above
public static void AccountLogin(AccountLoginEventArgs e)
{
// check if the account exists, if not, return
if (Accounts.GetAccount(e.Username) is not Account account)
{
return;
}
// check if account is young, if not we don't care
if (!account.Young)
{
return;
}
// remove the young status from the account
account.RemoveYoungStatus(0);
}
```
### Load Your Assembly
The file `Distribution/Data/assemblies.json` determines what files ModernUO will load as addons. Replace it with the following to include our file `AddonExample.dll`.
```json
[
"UOContent.dll",
"AddonExample.dll"
]
```
### Run It? I think
I haven't fixed my ModernUO to build on dotnet 9 yet.