Skip to content

Commit 239bb5f

Browse files
authored
Merge pull request #755 from gabrielgad/fix/game-0.10.34.28347-compatibility
Add holo beacon (marker) sync for multiplayer
2 parents e08a317 + 51c0db1 commit 239bb5f

10 files changed

Lines changed: 672 additions & 1 deletion

File tree

NebulaModel/Packets/Session/GlobalGameDataResponse.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public enum EDataType : byte
1717
SpaceSector,
1818
MilestoneSystem,
1919
TrashSystem,
20+
GalacticDigital,
2021
Ready
2122
}
2223

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace NebulaModel.Packets.Universe;
2+
3+
public class MarkerSettingUpdatePacket
4+
{
5+
public MarkerSettingUpdatePacket() { }
6+
7+
public MarkerSettingUpdatePacket(int planetId, int markerId, MarkerSettingEvent settingEvent, int intValue = 0,
8+
float floatValue = 0f, string stringValue = null, short[] colorData = null)
9+
{
10+
PlanetId = planetId;
11+
MarkerId = markerId;
12+
Event = settingEvent;
13+
IntValue = intValue;
14+
FloatValue = floatValue;
15+
StringValue = stringValue;
16+
ColorData = colorData;
17+
}
18+
19+
public int PlanetId { get; set; }
20+
public int MarkerId { get; set; }
21+
public MarkerSettingEvent Event { get; set; }
22+
public int IntValue { get; set; }
23+
public float FloatValue { get; set; }
24+
public string StringValue { get; set; }
25+
public short[] ColorData { get; set; }
26+
}
27+
28+
public enum MarkerSettingEvent
29+
{
30+
SetName = 0,
31+
SetWord = 1,
32+
SetTags = 2,
33+
SetTodoContent = 3,
34+
SetIcon = 4,
35+
SetColor = 5,
36+
SetVisibility = 6,
37+
SetDetailLevel = 7,
38+
SetHeight = 8,
39+
SetRadius = 9
40+
}

NebulaNetwork/PacketProcessors/Planet/FactoryLoadRequestProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@ static void OnPlanetFactoryLoad(PlanetData planet)
7676
buffer[id].HandleFullHp(GameMain.data, spaceSector.skillSystem);
7777
}
7878
}
79-
8079
}
80+
8181
}

NebulaNetwork/PacketProcessors/Session/GlobalGameDataRequestProcessor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#region
22

33
using NebulaAPI.Packets;
4+
using NebulaModel.Logger;
45
using NebulaModel.Networking;
56
using NebulaModel.Packets;
67
using NebulaModel.Packets.GameStates;
@@ -69,6 +70,14 @@ protected override void ProcessPacket(GlobalGameDataRequest packet, NebulaConnec
6970
GlobalGameDataResponse.EDataType.TrashSystem, writer.CloseAndGetBytes()));
7071
}
7172

73+
using (var writer = new BinaryUtils.Writer())
74+
{
75+
GameMain.data.galacticDigital.Export(writer.BinaryWriter);
76+
77+
conn.SendPacket(new GlobalGameDataResponse(
78+
GlobalGameDataResponse.EDataType.GalacticDigital, writer.CloseAndGetBytes()));
79+
}
80+
7281
using (var writer = new BinaryUtils.Writer())
7382
{
7483
writer.BinaryWriter.Write(GameMain.sandboxToolsEnabled);
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#region
2+
3+
using System.Linq;
4+
using NebulaAPI.Packets;
5+
using NebulaModel.Logger;
6+
using NebulaModel.Networking;
7+
using NebulaModel.Packets;
8+
using NebulaModel.Packets.Universe;
9+
using NebulaWorld;
10+
11+
#endregion
12+
13+
namespace NebulaNetwork.PacketProcessors.Universe;
14+
15+
[RegisterPacketProcessor]
16+
internal class MarkerSettingUpdateProcessor : PacketProcessor<MarkerSettingUpdatePacket>
17+
{
18+
// Cache for the Marker owner type (discovered via reflection)
19+
private static object _markerOwnerType;
20+
private static bool _markerOwnerTypeDiscovered;
21+
22+
/// <summary>
23+
/// Creates a TodoModule for a marker using galacticDigital.AddTodoModule().
24+
/// This properly registers the todo in the global pool.
25+
/// </summary>
26+
private static TodoModule CreateMarkerTodoModule(int markerId)
27+
{
28+
var galacticDigital = GameMain.data?.galacticDigital;
29+
if (galacticDigital == null)
30+
{
31+
Log.Warn("CreateMarkerTodoModule: galacticDigital is null");
32+
return null;
33+
}
34+
35+
try
36+
{
37+
var bindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
38+
var addTodoMethod = galacticDigital.GetType().GetMethod("AddTodoModule", bindingFlags);
39+
40+
if (addTodoMethod == null)
41+
{
42+
Log.Warn("CreateMarkerTodoModule: AddTodoModule method not found");
43+
return null;
44+
}
45+
46+
// Discover the Marker owner type if not already cached
47+
if (!_markerOwnerTypeDiscovered)
48+
{
49+
var ownerTypeEnum = addTodoMethod.GetParameters()[0].ParameterType;
50+
var enumValues = System.Enum.GetValues(ownerTypeEnum);
51+
52+
// Log all enum values for debugging
53+
Log.Debug($"CreateMarkerTodoModule: ETodoModuleOwnerType values: {string.Join(", ", enumValues.Cast<object>().Select(v => $"{v}={System.Convert.ToInt32(v)}"))}");
54+
55+
// Search for "Entity" type in the enum (markers use ETodoModuleOwnerType.Entity = 2)
56+
foreach (var val in enumValues)
57+
{
58+
var name = val.ToString();
59+
if (name == "Entity")
60+
{
61+
_markerOwnerType = val;
62+
Log.Debug($"CreateMarkerTodoModule: Found Entity owner type: {val}");
63+
break;
64+
}
65+
}
66+
67+
// If not found, log warning but don't fail
68+
if (_markerOwnerType == null)
69+
{
70+
Log.Warn("CreateMarkerTodoModule: Could not find Entity owner type in ETodoModuleOwnerType enum");
71+
}
72+
73+
_markerOwnerTypeDiscovered = true;
74+
}
75+
76+
if (_markerOwnerType == null)
77+
{
78+
return null;
79+
}
80+
81+
// Call AddTodoModule(markerOwnerType, markerId)
82+
Log.Debug($"CreateMarkerTodoModule: Calling AddTodoModule({_markerOwnerType}, {markerId})");
83+
var result = addTodoMethod.Invoke(galacticDigital, new object[] { _markerOwnerType, markerId });
84+
85+
if (result is TodoModule todoModule)
86+
{
87+
Log.Debug($"CreateMarkerTodoModule: Created TodoModule for marker {markerId}");
88+
return todoModule;
89+
}
90+
91+
Log.Warn($"CreateMarkerTodoModule: AddTodoModule returned {result?.GetType().Name ?? "null"} instead of TodoModule");
92+
return null;
93+
}
94+
catch (System.Exception ex)
95+
{
96+
Log.Warn($"CreateMarkerTodoModule: Exception - {ex.Message}\n{ex.StackTrace}");
97+
return null;
98+
}
99+
}
100+
101+
/// <summary>
102+
/// Finds an existing TodoModule for a marker in the galactic todos pool.
103+
/// </summary>
104+
private static TodoModule FindMarkerTodoInPool(int markerId)
105+
{
106+
var todos = GameMain.data?.galacticDigital?.todos;
107+
if (todos == null) return null;
108+
109+
for (int i = 1; i < todos.cursor; i++)
110+
{
111+
ref var todo = ref todos.buffer[i];
112+
if (todo.id == i && todo.ownerId == markerId)
113+
{
114+
// Verify it's a marker type (not planet or other)
115+
if (_markerOwnerType != null && System.Convert.ToInt32(todo.ownerType) == System.Convert.ToInt32(_markerOwnerType))
116+
{
117+
return todo;
118+
}
119+
}
120+
}
121+
122+
return null;
123+
}
124+
125+
protected override void ProcessPacket(MarkerSettingUpdatePacket packet, NebulaConnection conn)
126+
{
127+
Log.Debug($"MarkerSettingUpdatePacket received: Planet={packet.PlanetId}, Marker={packet.MarkerId}, Event={packet.Event}");
128+
129+
var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory;
130+
if (factory == null)
131+
{
132+
Log.Warn($"MarkerSettingUpdatePacket: Can't find factory for planet {packet.PlanetId}");
133+
return;
134+
}
135+
136+
var markers = factory.digitalSystem?.markers;
137+
if (markers == null || packet.MarkerId <= 0 || packet.MarkerId >= markers.cursor)
138+
{
139+
Log.Warn($"MarkerSettingUpdatePacket: Can't find marker ({packet.PlanetId}, {packet.MarkerId}), cursor={markers?.cursor}");
140+
return;
141+
}
142+
143+
ref var marker = ref markers.buffer[packet.MarkerId];
144+
if (marker.id != packet.MarkerId)
145+
{
146+
Log.Warn($"MarkerSettingUpdatePacket: Marker id mismatch ({packet.PlanetId}, {packet.MarkerId})");
147+
return;
148+
}
149+
150+
if (IsHost)
151+
{
152+
// Relay packet to other clients
153+
Server.SendPacketExclude(packet, conn);
154+
}
155+
156+
using (Multiplayer.Session.Warning.IsIncomingMarkerPacket.On())
157+
{
158+
switch (packet.Event)
159+
{
160+
case MarkerSettingEvent.SetName:
161+
marker.name = packet.StringValue;
162+
break;
163+
164+
case MarkerSettingEvent.SetWord:
165+
marker.word = packet.StringValue;
166+
break;
167+
168+
case MarkerSettingEvent.SetTags:
169+
marker.tags = packet.StringValue;
170+
break;
171+
172+
case MarkerSettingEvent.SetTodoContent:
173+
if (marker.todo != null)
174+
{
175+
marker.todo.content = packet.StringValue;
176+
// Apply color data if provided (colors and text are sent together)
177+
if (packet.ColorData != null)
178+
{
179+
marker.todo.contentColorIndex = packet.ColorData;
180+
}
181+
Log.Debug($"SetTodoContent: Updated todo for marker {packet.MarkerId}, colors={packet.ColorData?.Length ?? 0}");
182+
}
183+
else
184+
{
185+
// marker.todo is null - create via galacticDigital.AddTodoModule() for proper registration
186+
Log.Debug($"SetTodoContent: marker.todo is null for marker {packet.MarkerId}, creating via AddTodoModule");
187+
188+
// First check if there's already a todo in the pool for this marker
189+
var existingTodo = FindMarkerTodoInPool(packet.MarkerId);
190+
if (existingTodo != null)
191+
{
192+
// Link existing pool todo to marker and update content
193+
marker.todo = existingTodo;
194+
marker.todo.content = packet.StringValue;
195+
if (packet.ColorData != null)
196+
{
197+
marker.todo.contentColorIndex = packet.ColorData;
198+
}
199+
Log.Debug($"SetTodoContent: Found existing todo in pool, linked to marker {packet.MarkerId}");
200+
}
201+
else
202+
{
203+
// Create new todo via AddTodoModule
204+
var newTodo = CreateMarkerTodoModule(packet.MarkerId);
205+
if (newTodo != null)
206+
{
207+
marker.todo = newTodo;
208+
marker.todo.content = packet.StringValue;
209+
if (packet.ColorData != null)
210+
{
211+
marker.todo.contentColorIndex = packet.ColorData;
212+
}
213+
Log.Debug($"SetTodoContent: Created and linked TodoModule for marker {packet.MarkerId}");
214+
}
215+
else
216+
{
217+
Log.Warn($"SetTodoContent: Failed to create TodoModule for marker {packet.MarkerId}");
218+
}
219+
}
220+
}
221+
break;
222+
223+
case MarkerSettingEvent.SetIcon:
224+
marker.icon = packet.IntValue;
225+
break;
226+
227+
case MarkerSettingEvent.SetColor:
228+
marker.color = (byte)packet.IntValue;
229+
break;
230+
231+
case MarkerSettingEvent.SetVisibility:
232+
marker.visibility = (EMarkerVisibility)packet.IntValue;
233+
break;
234+
235+
case MarkerSettingEvent.SetDetailLevel:
236+
marker.detailLevel = (EMarkerDetailLevel)packet.IntValue;
237+
break;
238+
239+
case MarkerSettingEvent.SetHeight:
240+
marker.SetHeight(factory.entityPool, packet.FloatValue);
241+
break;
242+
243+
case MarkerSettingEvent.SetRadius:
244+
marker.SetRadius(packet.FloatValue);
245+
break;
246+
247+
default:
248+
Log.Warn($"MarkerSettingUpdatePacket: Unknown MarkerSettingEvent {packet.Event}");
249+
break;
250+
}
251+
252+
Log.Debug($"MarkerSettingUpdatePacket applied: Event={packet.Event}, icon={marker.icon}, color={marker.color}, name='{marker.name}'");
253+
254+
// Trigger visual update on the marker
255+
try
256+
{
257+
marker.InternalUpdate();
258+
}
259+
catch (System.Exception)
260+
{
261+
// InternalUpdate may fail if marker visuals aren't fully initialized yet
262+
}
263+
}
264+
}
265+
}

0 commit comments

Comments
 (0)