Commit graph

24 commits

Author SHA1 Message Date
Kamron Batman
8ec203f387
feat: Updates serialization to use MMF (considerable memory savings) (#1841)
### Summary
Updates the serialization strategy to use `MemoryMappedFile` instead of thick buffers. This has the benefit of being on-par with the current implementation (based on hardware/OS), however won't incur the double-memory issue.

> [!Important]
> **Developer Note**
> The `BinaryFileWriter` and `BinaryFileReader` has been removed in favor of `MemoryMapFileWriter` and `UnmanagedDataReader`
2024-06-23 10:51:20 -07:00
Kamron Batman
1f0acddc46
fix: Fixes serialization threading, moves world save to end of loop, and eliminates Parallel.ForEach. (#1530) 2023-10-03 00:42:49 -07:00
Kamron Batman
e0225c6e59
fix: Adds generic entity persistence support and BOBEntry as entities (#1527)
### Summary
Adds a generic entity persistence. This can be used to create new entity types that have a `Serial`.

Here is an example:

```cs
public class BOBEntries : GenericEntitySerialization<IBOBEntry>
{
    public static void Configure()
    {
        Configure("BOBEntries");
    }
}
```

The annotation tells the system what folder to serialize the entries to. The class/interface (`IBOBEntry`) is the root type that implements `ISerializable`.
2023-10-01 17:38:52 -07:00
Kamron Batman
8389bfacfe
chore: Updates copyright (#1448) 2023-08-09 09:09:26 -07:00
Kamron Batman
51ecee6caa
fix: Overhauls champion titles & codegen champion system (#1430)
## MAJOR CHANGE

Added a champion title system to facilitate the existing champion titles. This should make it easier to extend or create other related game content. Champion titles will be saved in a folder called _ChampionTitles_.

### Motivation

The motivation to refactor was two-folder, but mostly related to performance in two ways.
First, every player had a ChampionTitleInfo object with an array of ChamptionTitleInfo. We want to eliminate the need for this information unless a player actually uses it. This should save a considerable amount of memory.

Second, to facilitate the atrophy mechanic, the champion titles would run atrophy post-world save, adding to the time that the server is frozen. Eliminating this post-world save side effect unlocks our ability to further optimize the world save process since there are no direct side effects.


### Bugs fixed

- [X] Fixed titles getting cut off on the paperdoll
- [X] Fixed champion title not displaying overhead (OPL)

### Screenshots
<img width="216" alt="image" src="https://github.com/modernuo/ModernUO/assets/3953314/8916f895-8d68-4fb0-892e-108a0c43be90">
2023-07-27 21:44:06 -07:00
Kamron Batman
49e6c6f2d1
fix: Adds AfterSerialize support. Removes BeforeSerialize support. (#1208)
### Changes
* Implements an AfterSerialize method that is executed synchronously.
* Removes `BeforeSerialize` support since it was dangerous in its current implementation.
* Moves PlayerMobile kill/virtual decay to AfterSerialize.
* Adds kill decay to after Deserialize.
2022-10-27 23:27:22 -07:00
Kamron Batman
e1e30998ba
fix: Adds ReadType/Write(Type) and improves type referencing (#1172)
## Changes
* Improves type hashing by introducing xxHash3 (64bit)
* Removes individual `tdb` files in favor of a single `SerializedTypes.db` file. This file is only used to identify a type that is being deserialized, which doesn't exist.
* Adds duplicate type alias detection
* Adds `AssemblyHandler.FindTypeByHash`

View changed files whitespaces: https://github.com/modernuo/ModernUO/pull/1172/files?diff=split&w=1

## SerializedTypes.db
The serialized types file is used to get back the original name of a type in case it no longer exists in code. This can easily be necessary if a class is renamed in code and no `TypeAlias` is provided.

### Format
byte[4] - version
byte[4] - count
--array--
byte[8] - xxHash
byte[1] - flag, 0 - null, 1 - not null
byte[n] - Full class name in UTF8

### Example
<img width="472" alt="SerializedTypes_Example" src="https://user-images.githubusercontent.com/3953314/195255429-31d24293-6bd1-419e-811b-07874dd0f78d.png">

## Benchmarks
Serialized 500 Type fields. The 8192bytes comes from the _ConcurrentQueue_ that would later be used for SerializedTypes.
Note that the queue is never cleared, so it's size grew considerably.
```cs
|               Method |     Mean |    Error |   StdDev | Allocated |
|--------------------- |---------:|---------:|---------:|----------:|
|      BenchmarkXXHash | 18.44 us | 0.278 us | 0.260 us |    8192 B |
| BenchmarkTypeStrings | 25.09 us | 0.292 us | 0.259 us |         - |
```

TODO:
* Add support in the Serialization Generator for `ReadType()` and `Write(Type)`
* Remove `SetTypeRef` from Serialization Generator
2022-10-11 22:17:22 -07:00
Kamron Batman
f268d5d4e2
fix: Cleans up core code (#1187)
**Only one functional change**
* Fixes a bug in LogFactory where `Warning` is being logged as `Information`

Non-functional changes:
* Updates/Fixes copyright headers
* Removes namespace scopes for core files.

View with [whitespace off](https://github.com/modernuo/ModernUO/pull/1187/files?w=1).
2022-10-10 21:47:08 -07:00
Kamron Batman
fa5eafdaff
fix: Adds Created, LastSerialized, and BeforeSerialize for all entities. (#775)
* Adds `BeforeSerialized` for entities to handle cleanup.
* Adds `Created` and `LastSerialized` fields to all entities. While this is a big bloat, this will be necessary for identifying dangling references to other invalid entities.
* Changes formula for determining a valid reference to be _not null, not deleted, and reference's created date must be at or before the entities last serialized date_.
* Adds versioning to idx file and serializes `Created` and `LastSerialized`.
* Fixes Save Stats and also disables it by default.
2021-09-26 01:09:45 -07:00
Kamron Batman
360143478a
feat: Adds tidy option for serialization. Codegens ballotbox. Fixes pooled timer leaking (#681)
* Fixes pooled timer leaking
* Fixes `[dumptimers` command so it outputs properly, adds spacing, and stacktraces
* Adds `[Tidy]` for serializing Lists. This will remove deleted entities during world save before serializing the list.
* Adds helpers for managing Lists/Sets/Dictionaries


### New API

```cs
// Creates the list if it is null, then adds
Utility.Add(ref list, value);
Utility.Add(ref set, value);
Utility.Add(ref dict, key, value);

// Nulls the variable if the count is zero
Utility.Remove(ref list, value);
Utility.Remove(ref set, value);
Utility.Remove(ref dict, key);

// Marks entity as dirty in addition to doing the action
entity.Add(list, value);
// Marks entity as dirty, and will create list if it doesn't exist
entity.Add(ref list, value);

// Marks entity as dirty in addition to doing the action
entity.Remove(list, value);
// Marks entity as dirty, and will null the list count is zero
entity.Remove(ref list, value);
```


### Updates to [dumptimers
<img width="825" alt="Screen Shot 2021-08-14 at 2 55 10 AM" src="https://user-images.githubusercontent.com/3953314/129442449-ccf7fe14-29d6-4f3f-9366-c8eb7b9828a7.png">
2021-08-14 03:02:53 -07:00
Kamron Batman
81e4087acc
fix(codegen): Adds manual dirty checking (#621)
- Adds `[ManualDirtyChecking]` for anyone that adds it themselves.
- Adds detection of `[Serializable]` and `[ManualDirtyChecking]` at startup to help identify scripts that need migration.
2021-05-27 00:54:21 -07:00
Kamron Batman
312e3873f1
fix(core): Fixes encoded int. Adds dirty checking opt-out (#619)
### Additions
- Automatically opts-out `Item/Mobile/Guild/Accounts` from dirty checking with a new property `UseDirtyChecking`
- Codegen now enables `UseDirtyChecking` via getter. This requires that the property is `virtual` for derived types.

### Fixes
- Fixes `EncodedInt` being broken
- Fixes issues with new custom serializable types that are not derived from Item/Mobile/etc.
- Removes double dirty checking.

### Example of a brand new serializable type that isn't an Item/Mobile/etc.
User created code:
```cs
using System;

namespace Server.Items
{
    [Serializable(0)]
    public partial class NewTestEntityObject : ISerializable
    {
        [EncodedInt]
        [SerializableField(0)]
        private int _someProperty;

        public NewTestEntityObject()
        {
            SetTypeRef(GetType());
            // Add to serial tracking like World.Item
            /*
            Serial = World.NewEntity;
            World.AddEntity(this);
            */
        }

        [AfterDeserialization]
        private void AfterDeserialization()
        {
            Console.WriteLine("This ran!");
        }

        public int TypeRef { get; }
        public Serial Serial { get; }
        public void Delete()
        {
        }

        public bool Deleted { get; set; }
        public void SetTypeRef(Type type)
        {
            // Type tracking for persistence goes here
            /*
            TypeRef = World.NewEntityTypes.IndexOf(type);
            if (TypeRef == -1)
            {
                World.NewEntityTypes.Add(type);
                TypeRef = World.NewEntityTypes.Count - 1;
            }
            */
        }
    }
}
```

Generated code:
```cs
namespace Server.Items
{
    public partial class NewTestEntityObject
    {
#pragma warning disable 0414
        private const int _version = 0;
#pragma warning restore 0414

        public int SomeProperty
        {
            get => _someProperty;
            set
            {
                if (value != _someProperty)
                {
                    _someProperty = value;
                    ((ISerializable)this).MarkDirty();
                }
            }
        }

        long ISerializable.SavePosition { get; set; } = -1;
        BufferWriter ISerializable.SaveBuffer { get; set; }
        bool ISerializable.UseDirtyChecking => true;

        public NewTestEntityObject(Serial serial)
        {
            Serial = serial;
            SetTypeRef(typeof(NewTestEntityObject));
        }

        public void Serialize(IGenericWriter writer)
        {

            writer.WriteEncodedInt(_version);

            writer.WriteEncodedInt(SomeProperty);
        }

        public void Deserialize(IGenericReader reader)
        {
            var version = reader.ReadEncodedInt();

            SomeProperty = reader.ReadEncodedInt();

            Timer.DelayCall(AfterDeserialization);
        }
    }
}
```
2021-05-26 19:25:05 -07:00
Kamron Batman
1f499b00c6
fix(core): Fixes issues with deserialization due to bad checking of dirty tracking (#618)
Fixes a major issue where serialization was not respecting the dirty tracking feature flag.
2021-05-26 13:47:04 -07:00
Kamron Batman
9afa4e4cab
feat: Source generated Serialization/Deserialization (#550)
### Features
* Fully abstracts serialization by using compile-time attributes.
* Supports serializing the following:
  - Primitives (integers, strings, etc)
  - IP Addresses
  - BigDecimal
  - DateTime, Delta DateTimes
  - TimeSpan
  - Server.Race
  - Server.Map
  - Point2D, Point3D, Rect2D, Rect3D
  - Existing/New `ISerializable` references
  - Lists/Sets of serializable types
  - Type with a `Serialize` method and constructor that takes an `IGenericReader`
* Supports forward-only migration
* Supports existing RunUO deserialization for older versions by changing to the following signature:
  - `public void OldDeserialize(IGenericReader reader, int version)`
  - Must remove deserializing the version since this is already done
* Supports serializing from private fields or custom made properties.
* Types do not require inheriting Item/Mobile. Code gen will fully create `ISerializable` information.
  - This is not recommended yet, since it requires wiring to `Persistence` which will cause lots of unresolved symbol errors until code gen is built.

### Example
```cs
using System.Collections.Generic;

namespace Server.Items
{
    [Serializable(1)]
    public partial class TestItem1 : Item
    {
        [SerializableField(1)]
        [SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
        private List<Item> _someProperty;

        private void Deserialize(IGenericReader reader, int version)
        {
        }
    }
}
```

Generates this:
```cs
namespace Server.Items
{
    public partial class TestItem1
    {
#pragma warning disable 0414
        private const int _version = 1;
#pragma warning restore 0414

        [CommandProperty(AccessLevel.Administrator)]
        public System.Collections.Generic.List<Server.Item> SomeProperty
        {
            get => _someProperty;
            set
            {
                if (value != _someProperty)
                {
                    ((ISerializable)this).MarkDirty();
                    _someProperty = value;
                }
            }
        }

        public TestItem1(Serial serial) : base(serial)
        {
        }

        public override void Serialize(IGenericWriter writer)
        {
            var savePosition = ((Server.ISerializable)this).SavePosition;
            if (savePosition > -1)
            {
                writer.Seek(savePosition, System.IO.SeekOrigin.Begin);
                return;
            }
            writer.WriteEncodedInt(_version);
            writer.Write(_someProperty);
        }

        public override void Deserialize(IGenericReader reader)
        {
            var version = reader.ReadEncodedInt();
            if (version < 1)
            {
                OldDeserialize(reader, version);
                ((Server.ISerializable)this).MarkDirty();
                return;
            }
            SomeProperty = reader.ReadEntityList<Server.Item>();
        }
    }
}
```

And this:
```json
{
  "version": 1,
  "type": "TestItem1",
  "properties": [
    {
      "name": "SomeProperty",
      "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
      "rule": "ListMigrationRule",
      "ruleArguments": [
        "Server.Item",
        "SerializableInterfaceMigrationRule"
      ]
    }
  ]
}
```
2021-05-23 21:06:23 -07:00
Kamron Batman
4ddb3de026
fix(core): Fixes several serialization issues (#355)
- [X] Fixes an issue where a buffer smaller than 8 bytes would not double with enough space in some cases.
- [X] Fixes an issue with dupe copying the savebuffer reference (ugh).
- [X] Streamlines the IGenericWriter API to use better generics.
- [X] Streamlines the IGenericReader API to use better generics.
- [X] Forces `tidying` of a List/HashSet to be done externally since Writers/Readers should not have side effects.
- [X] Fixes an issue where Tidying a list didn't TrimExcess, causing memory leaks.
- [X] Reverted the meaning of `World.Running` to specifically refer to any world state post world loading.
  - NOTE: Do not use this if you want to block on world saves. Instead use checks against `WorldState.Saving` states.
- [X] Fixes an issue with serializing negative DateTime deltas.
- [X] Fixes a potential issue with serializing non-UTC DateTime.

Bumps release version
2020-12-23 07:11:41 -08:00
Kamron Batman
55935d30b4
Fixes bugs in world save (#297)
- [X] Fixes bugs in the buffer writer
- [X] Removes background save commands
- [X] Avoids calling Serialize() on deserialization
    - In a future optimization I will copy the entire object to a buffer, deserialize using a SpanReader, then use that buffer for the SaveBuffer
- [X] Fixes issue with decay queue.

Bumps release version
2020-11-01 17:22:18 -08:00
Kamron Batman
1ca8655fc1
Updates Serialization (#292)
- [X] Removes all save strategies
- [X] Removes duplicate file writer that won't be used
- [X] Removes persistence (it will become a duplicate system)
- [X] Add a save position variable to skip serializing a clean item/mobile
- [X] Update Guilds/Accounts to be IEntity types
    - Because guilds are abstract, this may make serialization tricky.
- [X] Update the generalized IEntity writing
- [X] Update the load/save to write to buffers then to files in background


Bumps release version
2020-10-31 17:38:29 -07:00
Kamron Batman
741e8d8300
Cleanup/Housekeeping (#242) 2020-09-12 15:31:21 -07:00
Kamron Batman
dad5997c85
Fixes copyright headers and live template (#216) 2020-09-03 01:47:20 -07:00
Kamron Batman
133a1c4b56
Renames buffer writer to fix any conflicts (#204) 2020-08-30 20:28:35 -07:00
Kamron Batman
86b7b3aed1
Fixes formatting (#200) 2020-08-25 18:53:35 -07:00
Kamron Batman
556a17aba8
Adds style cop (#109) 2020-04-26 00:16:02 -07:00
Kamron Batman
57b14ad690
Code Cleanup & Fixes Warnings (#74) 2020-01-18 14:56:45 -08:00
Leath Cooper
5c8821b11c Serialization interfaces (#62) 2019-12-30 12:16:35 -08:00