Currently the client and server side reference assemblies on NuGet have only minor differences in the API. It’d be nice to have a reference that is a superset of both APIs. That would allow using .NET scripts with shared_script directive in the manifest, with side checking done at runtime using IsDuplicityVersion function.
Note that while it’s possible to reference both assemblies, in some cases (usually when only shared functions are used, e.g. an empty BaseScript) the compiler decides to link only one assembly (the last -r argument).
Search for #if’s that depend on either IS_FXSERVER or GTA_FIVE shows that in
- client/clrcore/BaseScript.cs: protected property LocalPlayer must be stubbed on the server, lines 86–103
- client/clrcore/BaseScript.cs: TriggerServerEvent/TriggerClientEvent and TriggerLatentServerEvent/TriggerLatentClientEvent must be stubbed on server/client respectively, lines 217–L279
So that part is straightforward, e.g. something like the following patch:
diff.patch
diff --git a/code/client/clrcore/BaseScript.cs b/code/client/clrcore/BaseScript.cs
index 887392d27..e700cba27 100644
--- a/code/client/clrcore/BaseScript.cs
+++ b/code/client/clrcore/BaseScript.cs
@@ -100,6 +100,11 @@ namespace CitizenFX.Core
return m_player;
}
}
+#elif IS_FXSERVER && !IS_RDR3
+ protected Player LocalPlayer
+ {
+ get => null;
+ }
#endif
#if !IS_RDR3
@@ -214,26 +219,37 @@ namespace CitizenFX.Core
TriggerEventInternal(eventName, argsSerialized, false);
}
-#if !IS_FXSERVER
[SecuritySafeCritical]
public static void TriggerServerEvent(string eventName, params object[] args)
{
+#if !IS_FXSERVER
var argsSerialized = MsgPackSerializer.Serialize(args);
TriggerEventInternal(eventName, argsSerialized, true);
+#else
+ throw new NotSupportedException();
+#endif
}
[SecuritySafeCritical]
public static void TriggerLatentServerEvent(string eventName, int bytesPerSecond, params object[] args)
{
+#if !IS_FXSERVER
var argsSerialized = MsgPackSerializer.Serialize(args);
TriggerLatentServerEventInternal(eventName, argsSerialized, bytesPerSecond);
- }
#else
+ throw new NotSupportedException();
+#endif
+ }
+
public static void TriggerClientEvent(Player player, string eventName, params object[] args)
{
+#if IS_FXSERVER
player.TriggerEvent(eventName, args);
+#else
+ throw new NotSupportedException();
+#endif
}
/// <summary>
@@ -243,6 +259,7 @@ namespace CitizenFX.Core
/// <param name="args">Arguments to pass to the event.</param>
public static void TriggerClientEvent(string eventName, params object[] args)
{
+#if IS_FXSERVER
var argsSerialized = MsgPackSerializer.Serialize(args);
unsafe
@@ -252,11 +269,18 @@ namespace CitizenFX.Core
Function.Call(Hash.TRIGGER_CLIENT_EVENT_INTERNAL, eventName, "-1", serialized, argsSerialized.Length);
}
}
+#else
+ throw new NotSupportedException();
+#endif
}
public static void TriggerLatentClientEvent(Player player, string eventName, int bytesPerSecond, params object[] args)
{
+#if IS_FXSERVER
player.TriggerLatentEvent(eventName, bytesPerSecond, args);
+#else
+ throw new NotSupportedException();
+#endif
}
/// <summary>
@@ -266,6 +290,7 @@ namespace CitizenFX.Core
/// <param name="args">Arguments to pass to the event.</param>
public static void TriggerLatentClientEvent(string eventName, int bytesPerSecond, params object[] args)
{
+#if IS_FXSERVER
var argsSerialized = MsgPackSerializer.Serialize(args);
unsafe
@@ -275,8 +300,10 @@ namespace CitizenFX.Core
Function.Call(Hash.TRIGGER_LATENT_CLIENT_EVENT_INTERNAL, eventName, "-1", serialized, argsSerialized.Length, bytesPerSecond);
}
}
- }
+#else
+ throw new NotSupportedException();
#endif
+ }
#if !IS_FXSERVER
[SecurityCritical]
I don’t know what changes are needed in natives codegen though. In addition to that, we’d need to set up CI scripts for these reference assemblies.