Commit graph

75 commits

Author SHA1 Message Date
Kamron Batman
3bfd5c4d3e
fix: Adds CanSeeStaffOnly for staff only items (#739) 2021-08-27 19:12:32 -07:00
Kamron Batman
db59bf6dd0
fix(cleanup): Cleans up attribute syntax (#734) 2021-08-26 18:30:16 -07:00
Kamron Batman
3293ffcf06
fix: Cleanup IPoint3D calls (#704) 2021-08-19 09:11:48 -07:00
Kamron Batman
9b554f69b0
feat(timers): Adds timer pooling, fixes timer related bugs, and changes timer api (#667)
### Changes/Fixes:
* Adds timer pooling.
* Allows pool to be configurable in ModernUO.json
* Pool replenishes itself asynchronously if depleted.
* Fixes an issue with barkeeps and town criers
* Fixes an issue with incognito buff icons not being removed
* Fixes an issue with polymorph name mod not being removed
* Fixes several places where timers go on forever even after an object is deleted, keeping a reference (memory leak)
* Eliminates the timer for MiningCart altogether.
* Deletes `AcidSlime` since it is a duplicate of `PoolOfAcid`
* Fixes HonorableExecution and standardizes the code for other Bushido moves.

## Changes to the Timer API:
```cs
public class Timer
{
  // Creates a timer that will be returned to the pool once execution stops.
  public static void StartTimer(Action callback);
  public static void StartTimer(TimeSpan delay, Action callback);
  public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback);
  public static void StartTimer(TimeSpan interval, int count, Action callback);
  public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback);

  // Creates a timer and returns a token for more control. Requires manual cancellation in order for the timer to be returned to the pool.
  // If the token is dereferenced, the timer will be dereferenced too. While not returning a timer to the pool is not considered hazardous, it does defeat the purpose of pooled timers.
  public static void StartTimer(Action callback, out TimerExecutionToken token);
  public static void StartTimer(TimeSpan delay, Action callback, out TimerExecutionToken token);
  public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback, out TimerExecutionToken token);
  public static void StartTimer(TimeSpan interval, int count, Action callback, out TimerExecutionToken token);
  public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token);

  // If you aren't sure how to use the API above, or you don't care about performance, then you can use the old RunUO Timer.DelayCall
  public static DelayCallTimer DelayCall(Action callback);
  public static DelayCallTimer DelayCall(TimeSpan delay, Action callback);
  public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, Action callback);
  public static DelayCallTimer DelayCall(TimeSpan interval, int count, Action callback);
  public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback);
}

public struct TimerExecutionToken
{
  public bool Running { get; }
  public int Index { get; }
  public int RemainingCount { get; }
  public DateTime Next { get; }
}
```

## When to use `TimerExecutionToken`?
Use tokens when you want to gain the performance benefit of using a pooled timer, but you need one of the following:
* Access to the next time the timer will tick:`token.Next`
* Access to which interval, how many intervals there are, or how many are remaining: `token.Index`, `token.Count`, and `token.RemainingCount`
* Stop a timer manually.
* Determine if the timer is running: `timer.Running`
* See notes below about requirements for using tokens!

## Notes about using the TimerExecutionToken:
When you opt-in to receive a token, you must call `Cancel()` to return the timer. This can be done inside of the callback, or outside of the callback at any time.
If this is not called and your timer is an infinite interval, then you will create a potential memory leak, or null pointer exception in your callback.
If the timer ends and is stopped, but cancel is not called, then the timer will never return to the pool and stay referenced until the token is deleted or cancel is called. (Memory leak)

## Is this thread safe?
No. The ModernUO timer system is not thread safe at all. If you require a thread safe timer system, contact me and I'll help adapt this system. Keep in mind that there is a massive performance hit to make this thread safe when there are literally no use cases for it.

If you need to synchronize execution, meaning you want to execute code from another thread on the core thread. Let's say you have a discord bot that is pushing commands to the game server. Then use `EventLoopContext.Post(SendOrPostCallback callback, object state);`.
2021-08-07 14:33:35 -07:00
Kamron Batman
f78a226321
fix(core): Fixes packet issues with hair/beard (#669) 2021-08-01 19:54:04 -07:00
Kamron Batman
fb915992dd
fix(core): Adds Hashed & Hierarchical Timer Wheel (#655)
- Removes TimerPriority
- Removes TimerThread
- Adds a [Hashed & Hierarchical Timer Wheel](http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf)
2021-07-11 22:42:06 -07:00
Kamron Batman
61c49b8faa
fix(core): Fixes OPL with stale header (#651)
- [X] FIxes stale header/header args

Closes #650 
Closes #607
2021-06-11 23:44:05 -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
a0893b68c0
fix(core): Updates slice with range selectors (#583)
- [X] Replaces Span slicing with range selection
- [X] Replaces string slicing with range selection
2021-04-23 20:57:24 -07:00
Michael Whelehan
421d852991
fix(mobile): Make Criminal virtual and expose ExpireCriminalTimer property (#579) 2021-04-20 01:05:05 -07:00
Kamron Batman
529abb4283
fix(core): Fixes threading issue and adds threadguard (#565)
Setting `m.NetState = null` from a different thread caused `Mobile.Delta` and `NetState.Send` to run from a separate thread which cannot happen.
- [X] Fixes this issue
- [X] Adds THREADGUARD compiler constant as an option to trace this in the future.
2021-04-08 21:56:31 -07:00
Kamron Batman
c50322c0e3
fix(network): Adds UOG & Fixes ConnectUO packet (#552)
- [X] Adds UOG Extended and Compact (0xF1 0x51 packet)
- [X] Fixes ConnectUO bad length
2021-03-15 16:18:28 -07:00
Kamron Batman
6c4308bce6
fix(core): Caches DateTime.NowUtc (#548)
- [X] Caches DateTime.NowUtc on the game loop (not other threads)
- [X] Replaces all locations where it makes sense
- [X] Adds Min/Max for `IComparable` (TimeSpan, DateTimes, etc)

Closes #261
2021-03-13 01:32:04 -08:00
Kamron Batman
ec1cc10821
fix(network): Converts House Design Detailed Packet (#538)
- [X] Converts design state detailed packet
- [X] Removes `Packet` class
- [X] Removes `Send(Packet)` function signatures
- [X] Removes `PacketWriter` class
- [X] Updates dependencies.
2021-03-03 02:09:15 -08:00
Kamron Batman
ea5d09a7d7
fix(core): Fixes map issues at New Haven (#509)
- [X] Fixes map issues at New Haven by turning off static diffs
- [X] Some code cleanup and reformatting of tile matrix, tile matrix patch, and tile data
- [X] Adds NetState.Flush for generating spawners so it doesn't feel like the server is frozen
- [X] Reverts NativeReader changes from a while back.
- [X] Adds more string reading for BufferReader.


Notes:
BufferReader is still `little endian` compared to `SpanReader` which is `big endian` (for packets). To that end, the RunUO deserialization `ReadString()` was made obsolete since it is ambiguous, and contains extra fields other than simply reading a string. Furthermore, we shouldn't be using UTF8 (for now) since it is slow.
2021-02-14 16:06:49 -08:00
Kamron Batman
952a3e0265
fix(core): Handles a rare NPE with LoginTimer (#508) 2021-02-11 23:47:02 -08:00
Kamron Batman
8903028b5f
fix(core): Tightens the network stack (#479)
- [X] Removes network pause/resume
- [X] Adds back packet profiler
- [X] Adds state machine to keep track and trace netstates
- [X] Changes NetState.Running back to using an interlock exchange
- [X] Adds preliminary packet throttling support


### Packet Throttling
- `[GetThrottle <packetId>` to get the delay in milliseconds for that packet
- `[SetThrottle <packetId> <delay>` to set the delay in milliseconds for that packet
The settings are saved to `Configuration/throttles.json`
2021-02-07 23:30:56 -08:00
Kamron Batman
67625ad249
fix(core): Removes disposable netstates. (#467)
- [X] Removes IDisposable from NetState
- [X] Swaps Disconnect and Dispose methods
2021-02-05 14:33:35 -08:00
Kamron Batman
079eeb1b96
fix(core): Fixes nullables being disabled (#466) 2021-02-05 14:09:57 -08:00
Kamron Batman
5d9ebf062f
core(fix): Fixes type aliasing, categorizations, and NPE (#444)
- [X] Fixes lookup of type aliases
- [X] Fixes categorization
- [X] Removes rebuild categorizations
- [X] Fixes an NPE with DoHarmful and Combatant

Closes #441
2021-02-03 16:14:04 -08:00
Kamron Batman
c6e93a6814
fix(core): Fixes a few netstate issues (#436)
- [X] Fixes the order of disconnect/dispose
- [X] Removes IsDisposing
2021-02-01 20:37:51 -08:00
Kamron Batman
6f0e1a22f9
fix(core): Core cleanup, adds event loop synchronization, and fixes gumps (#429)
- [X] Cleans up gump packets
- [X] Adds event loop task synchronization
- [X] Moves timer pause and cleans up the delay task timer class
- [X] Cleans up the conserve cpu
- [X] Renames variables to make them consistent with the new style
- [X] Removes process delta recursion checking.
- [X] Properly diposes net states. This fixes edge cases that may cause hanging connections to stay open longer than they should.
- [X] Fixes an issue with gump items not compiling properly

Users can now utilize `Timer.Pause()` since the code will be executed on the proper thread.

```cs
public void async void Talk()
{
    _canTalk = false;
    await Timer.Pause(Utility.RandomMinMax(5000, 8000)); // Talk after 5-8 seconds
    DoTalk();
    await Timer.Pause(Utility.RandomMinMax(12000, 25000)); // Reset ability to talk after 12-25 seconds
    _canTalk = true;
}
```
2021-01-25 21:06:46 -08:00
Michael Whelehan
3cc09bf606
feat(mobile): Makes paralyze/freeze virtual (#423)
- [X] Makes paralyze/freeze virtual
2021-01-20 10:38:47 -08:00
Kamron Batman
b921c38879
fix(core): Removes some uses of Linq (#421)
- [X] Removes some uses of linq
- [X] Creates struct based enumerator for Skills
- [X] Creates a struct based enumerator for TypeCache
- [X] Changes HarvestDefinition to an init array instead of List
- [X] Changes HeritageTokenGump Response from List to Array
2021-01-18 21:05:11 -08:00
Kamron Batman
87764ca968
fix(build): Attempt to fix possible nullable warnings as errors with releases (#418) 2021-01-18 11:52:31 -08:00
Kamron Batman
c58aad733d
fix(core): Converts BB packets (#406)
- [X] Organizes and splits up bulletin boards
- [X] Converts packets
2021-01-13 22:28:31 -08:00
Kamron Batman
f4dd7c8e7f
fix(party): Converts party packets (#401)
- [X] Converts party packets
2021-01-10 14:23:10 -08:00
Kamron Batman
764d303543
chore: Code cleanup (#399)
- [X] Code cleanup
2021-01-10 09:14:03 -08:00
Kamron Batman
8763be8a21
fix(core): Cleans up uninitialized packets (#397)
- [X] Encapsulates/abstracts buffer cache checking
- [X] Changes mobile moving cache to use Span2D
2021-01-09 14:10:29 -08:00
Kamron Batman
0912876f7c
fix(core): Converts virtual hair packets (#388)
- [X] Converts virtual hair packets
2021-01-05 22:14:54 -08:00
Kamron Batman
3b413371dc
fix(core): Adds Gump component functionality (#377)
- [X] Adds gump close packet
- [X] Fixes gump tooltip
- [X] Adds new compile/append functions for the new gump packets
2021-01-02 19:08:35 -08:00
Kamron Batman
540a879d63
fix(core): Converts remaining player packets (#374)
- [X] Converts the remainder of the player packets
2020-12-30 16:09:51 -08:00
Kamron Batman
a59fda6a3a
fix(core): Converts some player packets (#372)
- [X] Converts some player packets

Note:
- Not caching weather packet because it is copying directly from stackalloc using aggressive inlining. Don't need to do more optimizations.
2020-12-29 23:47:56 -08:00
Kamron Batman
67c23bf9ad
fix(AI): Fixes direction issue with NPCs (#371)
- [X] Fixes a bug with RunUO where direction is set multiple times and the run flag is not set properly.

TODO: Fix other direction/movement issues and duplicate packets.

Bumps release version
2020-12-29 22:32:10 -08:00
Kamron Batman
7ef1d41c6f
fix(core): Converts mobile incoming packet (#370)
- [X] Converts mobile incoming packet

Bumps release version
2020-12-28 02:30:47 -08:00
Kamron Batman
9d4161b3e4
fix(core): Converts MobileUpdate packet (#369)
- [X] Converts mobile update packet
2020-12-28 01:50:17 -08:00
Kamron Batman
3d98e6e193
fix(core): Converts mobile status packets (#368)
- [X] Combines compact/extended statuses
2020-12-28 01:13:02 -08:00
Kamron Batman
8f8b650ab5
fix(core): Converts healthbar packets (#367)
- [X] Converts healthbar packets
2020-12-27 23:22:21 -08:00
Kamron Batman
26243a72a7
fix(core): Converts mobile name and animation packets (#366)
- [X] Converts mobile name
- [X] Converts animation
- [X] Updates some old tests
2020-12-27 19:57:38 -08:00
Kamron Batman
5b69f1d389
fix(core): Converts mobile stats packets (#360)
- [X] Converts mobile stats packets
- [X] Removes human racial from non-players
2020-12-27 14:44:03 -08:00
Kamron Batman
03d0a4664e
fix(core): Adds SkipLocalsInitAttribute support, optimizes broadcasted packets (#363)
- [X] Adds support for `SkipLocalsInitAttribute`. SkipLocalsInitAttribute skips initializing stack variables including `stackalloc`. I don't think it is wired/working yet but should be implemented soon.
- [X] Optimizes broadcasted packets by checking if the first byte (packet ID) is non-zero. If it is already set, it reuses the buffer.
- [X] Removes unnecessary refs to Spans. I don't think the Span struct itself is mutable, so a ref is not helpful.
2020-12-26 12:46:06 -08:00
Kamron Batman
10a6e17640
fix(core): Converts mobile moving packet (#357)
- [X] Converts mobile moving packet
2020-12-24 17:13:55 -08: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
90393fea2a
fix(core): Converts death animation (#356)
- [X] Converts death animation
- [X] Fixes tests for movement packets
2020-12-21 23:43:45 -08:00
Kamron Batman
77ce2e1980
fix(core): Optimizes strings / .NET 5 compatibility changes (#354)
- [X] Removes some string allocations (e.g. split)
- [X] Optimizes some collections
- [X] Converts insensitive to extension methods of built-ins.
- [X] Adds ordinal (case sensitive) string helpers
- [X] Fixes conditionals for in-game commands so they use Ordinal comparisons.
- [X] Replaces ToLower.Contains with InsensitiveContains
- [X] Adds ValueStringBuilder
- [X] Implements ValueStringBuilder in a few places where it makes sense
- [X] Removes the redundant Wrap function and replaces it with an optimized version
- [X] Fixes list conversions in Utility

Closes #351

Bumps release version
2020-12-20 23:21:55 -08:00
Kamron Batman
d71856ddbe
fix(net): Clean up for mobile packets (#338)
### Breaking Change
- [X] `Mobile.GetOldPacketFlags()` no longer exists. Instead there is a flag for `Mobile.GetPacketFlags()`.

### Non-Breaking Changes
- [X] Consolidates move to world for mobiles
- [X] Consolidates mobile packets between stygian abyss and older

This PR supersedes changes in #337. That PR will need to be redone and broken out anyway.
2020-12-10 17:46:58 -08:00
Kamron Batman
54b0a7dcb5
fix(core): Updates prompt packet (#332)
- [X] Updates prompt packet
2020-11-30 23:33:47 -08:00