private void CreateEventsFromAssemblies() { // Find methods with EventAttribute in any ISystem in any assembly. var events = new AssemblyScanner() .IncludeAllAssemblies() .IncludeNonPublicMembers() .Implements <ISystem>() .ScanMethods <EventAttribute>(); // Gather event data, compile invoker and add the data to the events collection. foreach (var(method, attribute) in events) { CoreLog.LogDebug("Adding event listener on {0}.{1}.", method.DeclaringType, method.Name); var name = attribute.Name ?? method.Name; if (!_events.TryGetValue(name, out var @event)) { _events[name] = @event = new Event(Invoke); } var argsPtr = 0; // The current pointer in the event arguments array. var parameterSources = method.GetParameters() .Select(info => new MethodParameterSource(info)) .ToArray(); // Determine the source of each parameter. foreach (var source in parameterSources) { var type = source.Info.ParameterType; if (typeof(Component).IsAssignableFrom(type)) { // Components are provided by the entity in the arguments array of the event. source.ParameterIndex = argsPtr++; source.IsComponent = true; } else if (type.IsValueType || DefaultParameterTypes.Contains(type)) { // Default types are passed straight trough. source.ParameterIndex = argsPtr++; } else { // Other types are provided trough Dependency Injection. source.IsService = true; } } var invoker = CreateInvoker(method, parameterSources, argsPtr); @event.Invokers.Add(invoker); } }
private void CreateTimersFromAssemblies(long tick) { // Find methods with TimerAttribute in any ISystem in any assembly. var events = new AssemblyScanner() .IncludeAllAssemblies() .IncludeNonPublicMembers() .Implements <ISystem>() .ScanMethods <TimerAttribute>(); // Create timer invokers and store timer info in registry. foreach (var(method, attribute) in events) { CoreLog.LogDebug("Adding timer on {0}.{1}.", method.DeclaringType, method.Name); if (!IsValidInterval(attribute.IntervalTimeSpan)) { CoreLog.Log(CoreLogLevel.Error, $"Timer {method} could not be registered the interval {attribute.IntervalTimeSpan} is invalid."); continue; } var service = _serviceProvider.GetService(method.DeclaringType); if (service == null) { CoreLog.Log(CoreLogLevel.Debug, "Skipping timer registration because service could not be loaded."); continue; } var parameterInfos = method.GetParameters() .Select(info => new MethodParameterSource(info) { IsService = true }) .ToArray(); var compiled = MethodInvokerFactory.Compile(method, parameterInfos); if (attribute.IntervalTimeSpan < LowInterval) { CoreLog.Log(CoreLogLevel.Warning, $"Timer {method.DeclaringType}.{method.Name} has a low interval of {attribute.IntervalTimeSpan}."); } var timer = new TimerInfo { IsActive = true, Invoke = () => compiled(service, null, _serviceProvider, null), IntervalTicks = attribute.IntervalTimeSpan.Ticks, NextTick = tick + attribute.IntervalTimeSpan.Ticks }; _timers.Add(timer); } }
public ServerCommandData Wait(Func <ServerCommandData, bool> accept = null) { ServerCommandData value; lock (_backlog) { if (accept == null) { if (_backlog.Count > 0) { value = _backlog[0]; _backlog.RemoveAt(0);// TODO: Improve O(n) performance return(value); } } else { for (var i = 0; i < _backlog.Count; i++) { if (accept(_backlog[i])) { value = _backlog[i]; _backlog.RemoveAt(i); return(value); } } } } for (;;) { // TODO: This is rather much of a workaround. It seems with certain timing the semaphore might stay in // TODO: the waiting state if it has been released too soon. In case that happens, the concurrent queue // TODO: will have the member added already. if (_asyncQueue.TryDequeue(out value)) { var ok = accept?.Invoke(value) ?? true; if (ok) { return(value); } CoreLog.LogDebug("command added to backlog"); lock (_backlog) { _backlog.Add(value); continue; } } _semaphore.Wait(); if (_asyncQueue.TryDequeue(out value)) { var ok = accept?.Invoke(value) ?? true; if (ok) { return(value); } CoreLog.LogDebug("command added to backlog"); lock (_backlog) { _backlog.Add(value); } } } }