Skip to content

Commit b2e4e8f

Browse files
kacper-dudziak-elympicsKrzysztof Pięta
authored andcommitted
feat: internal world replica state introduction
fix: initialize word first feat: review this mr: mr fixes chore: add custom replication interval ticks mr: mr fixes mr: add Replication test category and fix ReplicationPriority doc cref Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> mr: fix test category mr: fix mr chore: formatting chore: formatting fixes chore: fix tests
1 parent 2216381 commit b2e4e8f

85 files changed

Lines changed: 5734 additions & 299 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ csharp_wrap_arguments_style = chop_if_long
113113
max_line_length = 200
114114
csharp_keep_user_linebreaks = true
115115
csharp_space_between_method_call_name_and_opening_parenthesis = false
116+
dotnet_sort_system_directives_first = true
117+
dotnet_separate_import_directive_groups = true
118+
119+
dotnet_diagnostic.IDE0005.severity = warning
120+
121+
dotnet_diagnostic.IDE0055.severity = warning
116122

117123
### Add missing cases to switch expression
118124
dotnet_diagnostic.IDE0072.severity = suggestion

Editor/Config/ElympicsGameConfigEditor.uxml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<ui:TextField picking-mode="Ignore" label="Game name" binding-path="gameName" name="game-name" />
44
<ui:TextField picking-mode="Ignore" label="Game ID" binding-path="gameId" name="game-id" />
55
<ui:TextField picking-mode="Ignore" label="Game version" binding-path="gameVersion" name="game-version" />
6+
<ui:SliderInt picking-mode="Ignore" label="Max players" high-value="32" low-value="1" show-input-field="true" name="max-players" binding-path="maxPlayers" tooltip="Maximum number of players allowed in a match" />
67
<ui:GroupBox name="scene-group" style="padding-left: 0; padding-top: 0; padding-bottom: 0; margin-left: 0; margin-right: 0; margin-top: 3px; margin-bottom: 3px;">
78
<uie:ObjectField label="Gameplay scene" name="scene-object" type="UnityEditor.SceneAsset, UnityEditor" binding-path="gameplaySceneAsset" allow-scene-objects="false" style="flex-grow: 1;" />
89
<ui:TextField picking-mode="Ignore" label="Gameplay scene path" name="scene-path" binding-path="gameplayScene" readonly="true" style="-unity-text-align: upper-left;" />

Editor/CustomEditor/ElympicsBehaviourEditor.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ private enum ElympicsComponentsToSync
3131

3232
private SerializedProperty _visibleToPlayers;
3333

34-
private SerializedProperty _stateUpdateFrequencyStages;
34+
private SerializedProperty _replicationPriority;
35+
private SerializedProperty _netUpdateIntervalInTicks;
3536

3637
private StringBuilder _stringBuilder;
3738

@@ -57,8 +58,8 @@ private void OnEnable()
5758
_predictableToPlayers = serializedObject.FindProperty(nameof(_behaviour.predictableFor));
5859
_isUpdatableForNonOwners = serializedObject.FindProperty(nameof(_behaviour.isUpdatableForNonOwners));
5960
_visibleToPlayers = serializedObject.FindProperty(nameof(_behaviour.visibleFor));
60-
_stateUpdateFrequencyStages = serializedObject.FindProperty(nameof(_behaviour.stateFrequencyStages));
61-
61+
_replicationPriority = serializedObject.FindProperty(nameof(_behaviour.replicationPriority));
62+
_netUpdateIntervalInTicks = serializedObject.FindProperty(nameof(_behaviour.netUpdateIntervalInTicks));
6263
_stringBuilder = new StringBuilder();
6364
}
6465

@@ -78,7 +79,7 @@ public override void OnInspectorGUI()
7879
DrawNetworkId();
7980
DrawPredictability();
8081
DrawVisibility();
81-
DrawStateChangeFrequencyStages();
82+
DrawReplicationPriority();
8283
DrawObservedMonoBehaviours();
8384

8485
_ = serializedObject.ApplyModifiedProperties();
@@ -170,10 +171,12 @@ private void DrawVisibility()
170171
EditorGUILayout.Space();
171172
}
172173

173-
private void DrawStateChangeFrequencyStages()
174+
private void DrawReplicationPriority()
174175
{
175-
_ = EditorGUILayout.PropertyField(_stateUpdateFrequencyStages, new GUIContent(Label_StateUpdateFrequency, Label_StateUpdateFrequencyTooltip), true);
176-
EditorGUILayout.LabelField(Label_StateUpdateFrequencySummary, summaryLabelStyle);
176+
// ReplicationPriority hidden — BandwidthSchedulingSystem is pass-through, no effect yet.
177+
178+
_ = EditorGUILayout.PropertyField(_netUpdateIntervalInTicks, new GUIContent(Label_NetUpdateInterval, Label_NetUpdateIntervalTooltip), true);
179+
EditorGUILayout.LabelField(Label_NetUpdateIntervalSummary, summaryLabelStyle);
177180
EditorGUILayout.Space();
178181
}
179182

Editor/CustomEditor/ElympicsBehaviourEditorLabels.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ internal partial class ElympicsBehaviourEditor
3636

3737
private const string Label_VisibilityTooltip = "Choose which players will receive data about this object";
3838

39-
private const string Label_StateUpdateFrequency = "State update frequency: ";
39+
private const string Label_ReplicationPriority = "Replication Priority: ";
40+
private const string Label_ReplicationPrioritySummary = "Determines the relative importance of this entity for bandwidth scheduling. Higher-priority entities are sent first when bandwidth is limited.";
41+
private const string Label_ReplicationPriorityTooltip = "Priority ordering for bandwidth scheduling. Does not control re-send frequency.";
4042

41-
private const string Label_StateUpdateFrequencySummary = "Decide how often object state will be send in next snapshots";
42-
43-
private const string Label_StateUpdateFrequencyTooltip = "For the next X miliseconds, state will be updated once per Y miliseconds";
43+
private const string Label_NetUpdateInterval = "Net Update Interval (ticks): ";
44+
private const string Label_NetUpdateIntervalSummary = "Minimum ticks between unacknowledged re-sends. State changes are always sent immediately regardless of this setting.";
45+
private const string Label_NetUpdateIntervalTooltip = "How many ticks to wait before re-sending this entity when the client has not acknowledged receipt. Lower values = more aggressive re-sends.";
4446
}
4547
}

Editor/ElympicsBehaviourStateChangeFrequencyStageDrawer.cs

Lines changed: 0 additions & 54 deletions
This file was deleted.

Runtime/Behaviour/ElympicsBehaviour.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,8 @@ public sealed class ElympicsBehaviour : MonoBehaviour, IEquatable<ElympicsBehavi
2525
[SerializeField] internal ElympicsPlayer predictableFor = ElympicsPlayer.World;
2626
[SerializeField] internal bool isUpdatableForNonOwners;
2727
[SerializeField] internal ElympicsPlayer visibleFor = ElympicsPlayer.All;
28-
29-
[SerializeField]
30-
internal ElympicsBehaviourStateChangeFrequencyStage[] stateFrequencyStages =
31-
{
32-
new(500, 30),
33-
new(1000, 200),
34-
new(1000, 1000)
35-
};
28+
[SerializeField] internal ReplicationPriority replicationPriority = ReplicationPriority.Normal;
29+
[SerializeField] internal int netUpdateIntervalInTicks = 1;
3630

3731
private ElympicsComponentsContainer _componentsContainer;
3832

@@ -43,7 +37,6 @@ public sealed class ElympicsBehaviour : MonoBehaviour, IEquatable<ElympicsBehavi
4337
private List<ElympicsVar> _backingFields;
4438
private Dictionary<ElympicsVar, string> _backingFieldsNames;
4539
private List<(string, List<ElympicsVar>)> _backingFieldsByComponents;
46-
private ElympicsBehaviourStateChangeFrequencyCalculator _behaviourStateChangeFrequencyCalculator;
4740

4841
internal bool HasAnyState => _componentsContainer.Observables.Length > 0;
4942
internal bool HasAnyInput => _componentsContainer.InputHandler != null;
@@ -156,7 +149,28 @@ public ElympicsPlayer PredictableFor
156149
internal ElympicsBase ElympicsBase { get; private set; }
157150
public bool IsPredictableTo(ElympicsPlayer player) => predictableFor == ElympicsPlayer.All || player == predictableFor || player == ElympicsPlayer.World;
158151
public bool IsOwnedBy(ElympicsPlayer player) => IsPredictableTo(player);
159-
internal bool IsVisibleTo(ElympicsPlayer player) => visibleFor == ElympicsPlayer.All || player == visibleFor || player == ElympicsPlayer.World;
152+
internal bool IsVisibleTo(ElympicsPlayer player)
153+
{
154+
// World (server) can always see everything
155+
if (player == ElympicsPlayer.World)
156+
return true;
157+
158+
// If replication world is initialized and entity is registered, use bitmask
159+
var world = Replication.ElympicsWorld.Current;
160+
if (world != null)
161+
{
162+
var playerIdx = (int)player;
163+
if (playerIdx >= 0)
164+
{
165+
var d = world.GetDenseIndex(networkId);
166+
if (d >= 0)
167+
return (world.InterestMask[d] & (1u << playerIdx)) != 0;
168+
}
169+
}
170+
171+
// Fallback: original logic
172+
return visibleFor == ElympicsPlayer.All || player == visibleFor;
173+
}
160174

161175
private MemoryStream _memoryStream1;
162176
private MemoryStream _memoryStream2;
@@ -203,11 +217,6 @@ public bool TryGetInput(ElympicsPlayer player, out IInputReader inputReader, int
203217
}
204218

205219
#if UNITY_EDITOR
206-
private void OnValidate()
207-
{
208-
_behaviourStateChangeFrequencyCalculator?.ResetStateUpdateFrequencyStage();
209-
}
210-
211220
private void OnDrawGizmos()
212221
{ }
213222
#endif
@@ -222,8 +231,6 @@ internal void InitializeInternal(ElympicsBase elympicsBase)
222231

223232
ElympicsBase = elympicsBase;
224233

225-
_behaviourStateChangeFrequencyCalculator = new ElympicsBehaviourStateChangeFrequencyCalculator(stateFrequencyStages, AreStatesEqual, elympicsBase.Config);
226-
227234
_componentsContainer = new ElympicsComponentsContainer(this);
228235

229236
foreach (var observable in _componentsContainer.Observables)
@@ -303,11 +310,6 @@ internal byte[] GetState()
303310
return metadata;
304311
}
305312

306-
internal bool UpdateCurrentStateAndCheckIfSendCanBeSkipped(byte[] currentState, long tick)
307-
{
308-
return _behaviourStateChangeFrequencyCalculator.UpdateNextStateAndCheckIfSendCanBeSkipped(currentState, tick);
309-
}
310-
311313
internal void ApplyState(byte[] data, bool ignoreTolerance = false)
312314
{
313315
_memoryStream1.Write(data, 0, data.Length);

Runtime/Behaviour/ElympicsBehaviourStateChangeFrequencyCalculator.cs

Lines changed: 0 additions & 111 deletions
This file was deleted.

Runtime/Behaviour/ElympicsBehaviourStateChangeFrequencyStage.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

Runtime/Behaviour/ElympicsBehavioursContainer.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,20 @@ internal class ElympicsBehavioursContainer
1111
private readonly SortedDictionary<int, ElympicsBehaviour> _elympicsBehavioursPredictable = new();
1212
private readonly SortedDictionary<int, ElympicsBehaviour> _elympicsBehavioursUnpredictable = new();
1313

14-
/// <summary>Dictionary where values are ElympicBehaviours and keys are their network IDs.</summary>
14+
/// <summary>Dictionary where values are ElympicsBehaviours and keys are their network IDs.</summary>
1515
/// <remarks>This dictionary is sorted by network ID in ascending order.</remarks>
1616
public IReadOnlyDictionary<int, ElympicsBehaviour> Behaviours => _elympicsBehaviours;
17-
/// <summary>Dictionary where values are ElympicBehaviours for which <see cref="ElympicsBehaviour.HasAnyInput"/> is true and keys are their network IDs.</summary>
17+
/// <summary>Dictionary where values are ElympicsBehaviours for which <see cref="ElympicsBehaviour.HasAnyInput"/> is true and keys are their network IDs.</summary>
1818
/// <remarks>This dictionary is sorted by network ID in ascending order.</remarks>
1919
public IReadOnlyDictionary<int, ElympicsBehaviour> BehavioursWithInput => _elympicsBehavioursWithInput;
20-
/// <summary>Dictionary where values are ElympicBehaviours that are predictable for <see cref="_player"/> and keys are their network IDs.</summary>
20+
/// <summary>Dictionary where values are ElympicsBehaviours that are predictable for <see cref="_player"/> and keys are their network IDs.</summary>
2121
/// <remarks>This dictionary is sorted by network ID in ascending order.</remarks>
2222
public IReadOnlyDictionary<int, ElympicsBehaviour> BehavioursPredictable => _elympicsBehavioursPredictable;
23-
/// <summary>Dictionary where values are ElympicBehaviours that are not predictable for <see cref="_player"/> and keys are their network IDs.</summary>
23+
/// <summary>Dictionary where values are ElympicsBehaviours that are not predictable for <see cref="_player"/> and keys are their network IDs.</summary>
2424
/// <remarks>This dictionary is sorted by network ID in ascending order.</remarks>
2525
public IReadOnlyDictionary<int, ElympicsBehaviour> BehavioursUnpredictable => _elympicsBehavioursUnpredictable;
2626

27-
public ElympicsBehavioursContainer(ElympicsPlayer player)
28-
{
29-
_player = player;
30-
}
27+
public ElympicsBehavioursContainer(ElympicsPlayer player) => _player = player;
3128

3229
public bool Contains(int networkId) => _elympicsBehaviours.ContainsKey(networkId);
3330

0 commit comments

Comments
 (0)