Skip to content

Commit

Permalink
1.4.1:
Browse files Browse the repository at this point in the history
Delegate references are now stored by the reference string rather than
the delegate, to prevent possible collisions.
Fixed lifetime leases of remote objects expiring when still in use.
  • Loading branch information
DorCoMaNdO committed May 25, 2017
1 parent 22a0618 commit 05d0d47
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 39 deletions.
5 changes: 5 additions & 0 deletions ServerWrapper/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,10 @@ public static implicit operator Player(Client c)
{
c.Touch();
}*/

/*public override object InitializeLifetimeService()
{
return null;
}*/
}
}
2 changes: 1 addition & 1 deletion ServerWrapper/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyVersion("1.4.1.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
18 changes: 15 additions & 3 deletions ServerWrapper/ScriptTimer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CitizenMP.Server;
using System;
using System.Linq;
using System.Runtime.Remoting.Lifetime;

namespace ServerWrapper
{
Expand Down Expand Up @@ -45,7 +46,12 @@ internal ScriptTimer(ServerScript caller, int interval, ScriptTimerHandler callb

ID = Guid.NewGuid().ToString();

lock (Wrapper.ScriptTimers) while (Wrapper.ScriptTimers.Any(st => st.ID == ID)) ID = Guid.NewGuid().ToString(); // Not taking any chances.
lock (Wrapper.ScriptTimers)
{
while (Wrapper.ScriptTimers.Any(st => st.ID == ID)) ID = Guid.NewGuid().ToString(); // Not taking any chances.

Wrapper.ScriptTimers.Add(this);
}

SEScriptTimer = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Wrapper.SEScriptTimer.Assembly.FullName, Wrapper.SEScriptTimer.FullName);

Expand Down Expand Up @@ -74,7 +80,7 @@ public void Start()
{
TickFrom = Time.CurrentTime + Interval;

lock (Wrapper.ScriptTimers) if (!Wrapper.ScriptTimers.Contains(this)) Wrapper.ScriptTimers.Add(this);
//lock (Wrapper.ScriptTimers) if (!Wrapper.ScriptTimers.Contains(this)) Wrapper.ScriptTimers.Add(this);

lock (Wrapper.SEScriptTimerList) Wrapper.SEScriptTimerList.Add(SEScriptTimer);
}
Expand All @@ -85,13 +91,19 @@ public void Cancel()

lock (Wrapper.SEScriptTimerList) while (Wrapper.SEScriptTimerList.Contains(SEScriptTimer)) Wrapper.SEScriptTimerList.Remove(SEScriptTimer);

lock (Wrapper.ScriptTimers) Wrapper.ScriptTimers.RemoveAll(st => st == this || st.ID == ID);
//lock (Wrapper.ScriptTimers) Wrapper.ScriptTimers.RemoveAll(st => st == this || st.ID == ID);
}

public void Dispose()
{
Cancel();

lock (Wrapper.ScriptTimers) Wrapper.ScriptTimers.RemoveAll(st => st == this || st.ID == ID);

//RemotingServices.Disconnect(this);

((ILease)GetLifetimeService()).Unregister(Wrapper.instance);

caller.RemoveScriptTimerHandler(this);
}
}
Expand Down
33 changes: 22 additions & 11 deletions ServerWrapper/ServerScript.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Lifetime;

namespace ServerWrapper
{
Expand All @@ -23,7 +23,7 @@ public abstract class ServerScript : MarshalByRefObject, IServerScript
private Dictionary<ScriptTimer, ScriptTimerHandler> TimerHandlers = new Dictionary<ScriptTimer, ScriptTimerHandler>();
private Dictionary<string, List<Delegate>> EventHandlers = new Dictionary<string, List<Delegate>>();

private static Dictionary<Delegate, string> DelegateReferences = new Dictionary<Delegate, string>();
private static Dictionary<string, Delegate> DelegateReferences = new Dictionary<string, Delegate>();

public int Interval { get { return timer != null ? timer.Interval : 100; } set { if (timer != null) timer.Interval = value; } }

Expand Down Expand Up @@ -69,6 +69,8 @@ internal void CreateProxy(Wrapper wrapper, Queue<IServerScript> scripts)
t.Loop = false; // First ScriptTimer with Print and a call to Loop (usually) causes a small hiccup of 32~150ms for whatever reason, better have it done ahead of time.

w.ETPhoneHome(this, scripts);

t.Dispose();
});
}

Expand Down Expand Up @@ -146,9 +148,10 @@ public Player GetPlayerFromID(int ID)

public void TriggerClientEvent(string eventname, int netID, params object[] args)
{
if (w != null) w.TriggerClientEvent(eventname, netID, ConvertArgsFromLocal(args));
object[] cargs = ConvertArgsFromLocal(args);
if (w != null) w.TriggerClientEvent(eventname, netID, cargs);

lock (DelegateReferences) foreach (object arg in args) if (arg.GetType().IsSubclassOf(typeof(Delegate))) if (DelegateReferences.ContainsKey((Delegate)arg)) DelegateReferences.Remove((Delegate)arg);
lock (DelegateReferences) foreach (object arg in cargs) if (arg.GetType().IsSubclassOf(typeof(String))) if (DelegateReferences.ContainsKey((string)arg)) DelegateReferences.Remove((string)arg);
}

public void RegisterServerEvent(string eventname)
Expand All @@ -159,9 +162,10 @@ public void RegisterServerEvent(string eventname)
public bool TriggerEvent(string eventname, params object[] args)
{
bool notcanceled = false;
if (w != null) notcanceled = w.TriggerEvent(eventname, ConvertArgsFromLocal(args));
object[] cargs = ConvertArgsFromLocal(args);
if (w != null) notcanceled = w.TriggerEvent(eventname, cargs);

lock (DelegateReferences) foreach (object arg in args) if (arg.GetType().IsSubclassOf(typeof(Delegate))) if (DelegateReferences.ContainsKey((Delegate)arg)) DelegateReferences.Remove((Delegate)arg);
lock (DelegateReferences) foreach (object arg in cargs) if (arg.GetType().IsSubclassOf(typeof(String))) if (DelegateReferences.ContainsKey((string)arg)) DelegateReferences.Remove((string)arg);

return notcanceled;
}
Expand Down Expand Up @@ -199,6 +203,10 @@ public string SetTimeout(int delay, ScriptTimerHandler callback, bool loop = fal
{
ScriptTimer timer = w.CreateTimer(this, delay, loop);

if (timer == null) return null;

((ILease)timer.GetLifetimeService()).Register(w);

if (!TimerHandlers.ContainsKey(timer)) TimerHandlers.Add(timer, callback);
//TimerHandlers[timer] = callback;

Expand Down Expand Up @@ -260,11 +268,11 @@ internal object[] ConvertArgsToLocal(params object[] args)

lock (DelegateReferences)
{
if (DelegateReferences.ContainsValue(s))
if (DelegateReferences.ContainsKey(s))
{
Delegate d = DelegateReferences.Where(kv => kv.Value == s).ElementAt(0).Key;
Delegate d = DelegateReferences[s];

//DelegateReferences.Remove(d);
//DelegateReferences.Remove(s);

Converted.Add(d);

Expand Down Expand Up @@ -293,9 +301,12 @@ internal object[] ConvertArgsFromLocal(params object[] args)

lock (DelegateReferences)
{
if (!DelegateReferences.ContainsKey(d)) DelegateReferences.Add(d, type.ToString() + "|" + Guid.NewGuid().ToString());
string dref = type.ToString() + "|" + Guid.NewGuid().ToString() + "|" + d.GetHashCode();
while (DelegateReferences.ContainsKey(dref)) dref = type.ToString() + "|" + Guid.NewGuid().ToString() + "|" + d.GetHashCode();

DelegateReferences.Add(dref, d);

Converted.Add(DelegateReferences[d]);
Converted.Add(dref);
}

continue;
Expand Down
66 changes: 42 additions & 24 deletions ServerWrapper/Wrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Remoting.Lifetime;
using System.Threading;

namespace ServerWrapper
Expand All @@ -22,7 +23,7 @@ internal enum PrintType
Fatal
}

internal class Wrapper : MarshalByRefObject
internal class Wrapper : MarshalByRefObject, ISponsor
{
private static bool initialized = false;

Expand Down Expand Up @@ -270,8 +271,8 @@ public static void Initialize()
w.Proxy = null;
w.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); // Fails without a user-agent header.

JObject data = JObject.Parse(w.DownloadString("https://api.github.com/repos/DorCoMaNdO/FiveMServerWrapper/releases/latest"));
string[] ver = data["tag_name"].ToString().Split('.');
JObject json = JObject.Parse(w.DownloadString("https://api.github.com/repos/DorCoMaNdO/FiveMServerWrapper/releases/latest"));
string[] ver = json["tag_name"].ToString().Split('.');
if (ver.Length < 1 || !int.TryParse(ver[0], out NewMajor)) NewMajor = 1;
if (ver.Length < 2 || !int.TryParse(ver[1], out NewMinor)) NewMinor = 2;
if (ver.Length < 3 || !int.TryParse(ver[2], out NewBuild)) NewBuild = 0;
Expand Down Expand Up @@ -458,26 +459,30 @@ private static void LoadScripts(Queue<IServerScript> scripts)
{
if (scripts.Count > 0)
{
IServerScript script = scripts.Dequeue();
IServerScript iscript = scripts.Dequeue();

ServerScript ss = ((ServerScript)script);
ServerScript script = (ServerScript)iscript;

ss.timer = new ScriptTimer(ss, 100, (timer) =>
((ILease)script.GetLifetimeService()).Register(instance);

script.timer = new ScriptTimer(script, 100, (timer) =>
{
try
{
script.Tick();
iscript.Tick();
}
catch (Exception e)
{
instance.Print(PrintType.Error, "\"" + script.Name + "\"'s Tick() failed.");
instance.Print(PrintType.Error, "\"" + iscript.Name + "\"'s Tick() failed.");
instance.PrintException(e);
}
}, true);

instance.Print("Creating proxy for script \"" + script.Name + "\"...");
((ILease)script.timer.GetLifetimeService()).Register(instance);

instance.Print("Creating proxy for script \"" + iscript.Name + "\"...");

ss.CreateProxy(instance, scripts);
script.CreateProxy(instance, scripts);
}
}

Expand Down Expand Up @@ -533,47 +538,50 @@ private static void Unload(string path)
ScriptTimer[] timers;
lock (ScriptTimers) timers = ScriptTimers.ToArray();

foreach (IServerScript script in oldscripts)
foreach (IServerScript iscript in oldscripts)
{
foreach (ScriptTimer st in timers.Where(st => st.caller == script)) st.Dispose();
foreach (ScriptTimer st in timers.Where(st => st.caller == iscript)) st.Dispose();

lock (scripteventhandlers)
{
if (scripteventhandlers.ContainsKey(script))
if (scripteventhandlers.ContainsKey(iscript))
{
foreach (string eventname in scripteventhandlers[script].Keys)
foreach (string eventname in scripteventhandlers[iscript].Keys)
{
try
{
instance.RemoveAllEventHandlers((ServerScript)script, eventname);
instance.RemoveAllEventHandlers((ServerScript)iscript, eventname);

((ServerScript)script).RemoveAllEventHandlers(eventname);
((ServerScript)iscript).RemoveAllEventHandlers(eventname);
}
catch (Exception e)
{
instance.Print(PrintType.Error, "Failed to remove \"" + script.Name + "\"'s event handlers for event \"" + eventname + "\".");
instance.Print(PrintType.Error, "Failed to remove \"" + iscript.Name + "\"'s event handlers for event \"" + eventname + "\".");
instance.PrintException(e);
}
}

scripteventhandlers[script].Clear();
scripteventhandlers[iscript].Clear();

scripteventhandlers.Remove(script);
scripteventhandlers.Remove(iscript);
}
}

ScriptTimer t = ((ServerScript)script).timer;
if (t != null) t.Dispose();
ServerScript script = ((ServerScript)iscript);

if (script.timer != null) script.timer.Dispose();

try
{
script.Unload();
iscript.Unload();
}
catch (Exception e)
{
instance.Print(PrintType.Error, "\"" + script.Name + "\"'s Unload() failed.");
instance.Print(PrintType.Error, "\"" + iscript.Name + "\"'s Unload() failed.");
instance.PrintException(e);
}

((ILease)script.GetLifetimeService()).Unregister(instance);
}

lock (scripteventhandlers) scripteventhandlers.Clear();
Expand Down Expand Up @@ -717,7 +725,7 @@ internal ScriptTimer CreateTimer(ServerScript caller, int delay, bool loop = fal
{
ScriptTimer st = new ScriptTimer(caller, delay, (timer) => { caller.CallScriptTimerHandler(timer); }, loop);

return st;
return st.caller == caller ? st : null;
}

internal bool HasTimeoutFinished(ServerScript caller, string id)
Expand Down Expand Up @@ -930,5 +938,15 @@ internal void SetMapName(string mapName)
{
if (RSFSetMapName != null) RSFSetMapName.Invoke(null, new object[] { mapName });
}

public TimeSpan Renewal(ILease lease)
{
return new TimeSpan(0, 5, 0);
}

public override object InitializeLifetimeService()
{
return null;
}
}
}

0 comments on commit 05d0d47

Please sign in to comment.