• 中文
  • 注册
  • 技术交流 技术交流 关注:3 内容:13

    OpenMod插件的事件相关

  • 查看作者
  • 打赏作者
  • 当前位置: 未转变者中文社区 > 技术交流 > 正文
    • 技术交流
    • 搬运自OpenMod官网,经过翻译,可能有些不足,欢迎指正!原文链接

      事件和事件侦听器

      事件用于通知组件正在发生某些事情,如用户断开连接。

      有两种类型的事件:

           OpenMod事件

           C#事件

      本指南将介绍OpenMod事件。

      订阅事件

      有两种方式去订阅事件:

           实现IEventListener接口:

      public class UserConnectListener : IEventListener<UserConnectedEvent>
      {
          [EventListener(Priority = EventListenerPriority.Lowest)]
          public async Task HandleEventAsync(object sender, UserConnectEvent @event)
          {
              // 做点什么
          }
      }

           使用IEventBus服务的订阅方法:

      public class MyPlugin : OpenModUniversalPlugin
      {
          private readonly IEventBus m_EventBus;
           
          public MyPlugin(IEventBus eventBus, IServiceProvider serviceProvider) : base(serviceProvider)
          {
              m_EventBus = eventBus;
          }
           
          public async Task OnLoadAsync()
          {
              m_EventBus.Subscribe(this, (sender, @event) => {
                  // 做点什么
              });
          }
      }

      当插件卸载时,所有OpenMod事件侦听器都将自动取消订阅。您不必在卸载时手动取消订阅。

      事件侦听器优先级和执行顺序

      OpenMod允许您控制事件侦听器的执行顺序。执行顺序基于优先级。

      您可以使用[EventListener]属性设置事件侦听器优先级。

      执行顺序是从最低优先级到最高优先级。换句话说,最低优先级被称为第一优先级。

      public class UserConnectListener1 : IEventListener<UserConnectedEvent>
      {
          [EventListener(Priority = EventListenerPriority.Lowest)]
          public async Task HandleEventAsync(object sender, UserConnectEvent @event)
          {
       
          }
      }
      public class UserConnectListener2 : IEventListener<UserConnectedEvent>
      {
          [EventListener(Priority = EventListenerPriority.Low)]
          public async Task HandleEventAsync(object sender, UserConnectEvent @event)
          {
       
          }
      }
       
      public class UserConnectListener3 : IEventListener<UserConnectedEvent>
      {
          [EventListener(Priority = EventListenerPriority.High)]
          public async Task HandleEventAsync(object sender, UserConnectEvent @event)
          {
       
          }
      }

      在上面的示例中,首先调用UserConnectListener1,然后调用UserConnectListener2,最后调用UserConnectListener3

      取消事件和忽略取消的事件

      事件必须实现ICancellableEvent接口才能取消。如果事件被取消,[EventListener]属性中没有IgnoreCancelled设置为true的事件侦听器,将不会收到通知。

      UserConnectingEvent就是这样一个可取消的事件。如果事件被取消,它将断开连接用户的连接。

      public class UserConnectingListener1 : IEventListener<UserConnectingEvent>
      {
          [EventListener(Priority = EventListenerPriority.Lowest)]
          public async Task HandleEventAsync(object sender, UserConnectingEvent @event)
          {
              if(user.DisplayName.Equals("Trojaner"))
              {
                  @event.IsCancelled = true;
              }
          }
      }
       
      public class UserConnectingListener2 : IEventListener<UserConnectingEvent>
      {
          [EventListener(Priority = EventListenerPriority.Low)]
          public async Task HandleEventAsync(object sender, UserConnectingEvent @event)
          {
              // 此事件侦听器将不被调用,因为它不忽略取消
          }
      }
       
      public class UserConnectingListener3 : IEventListener<UserConnectingEvent>
      {
          [EventListener(Priority = EventListenerPriority.High, IgnoreCancelled = true)]    
            public async Task HandleEventAsync(object sender, UserConnectingEvent @event)
          {
              // 即使事件被取消,也会调用此事件侦听器
          }
      }

      在上面的示例中,如果名为“Trojaner”的用户连接,UserConnectingListener1将取消该事件。在这种情况下,不会调用UserConnectingListener2,因为它不会像UserConnectingListener3那样忽略取消的事件。

      事件侦听器生存期

      事件侦听器可以有三种类型的生存期:

            Transient – 总是在每个事件上重新创建事件侦听器。如果您有多个IEventListener,那么所有IEventListener都有自己的实例。这是默认的生存期。

            Scoped – 如果在一个类中实现多个IEventListener,那么所有IEventListener都将共享同一个实例。否则与Transient相同。

            Singleton – 事件侦听器只有一个共享生存期,直到插件卸载为止。

      您可以通过添加[EventListenerLifetime(ServiceLifetime)]属性来设置事件侦听器生存期:

      [EventListenerLifetime(ServiceLifetime.Transient)]
      public class UserConnectBroadcaster : IEventListener<UserConnectedEvent>
      // ...

      自定义事件

      创建自定义事件很简单:只需创建一个从Event继承的新类。

      举个例子:

      public class SampleEvent : Event
      {
          public int MyValue { get; set; } 
          // 也可以添加其他属性
      }

      然后,可以使用事件总线发出它:

      MyPlugin myPlugin = ...;
      IEventBus eventBus = ...;
      ILogger<xxx> logger = ...;
       
      var @event = new SampleEvent
      {
          MyValue = 20
      };
         
      await m_EventBus.EmitAsync(myPlugin, this /* 发送人 */, @event);
      logger.LogInformation($"Event value: {@event.MyValue}");

      如果希望事件可取消,则必须实现ICancellableEvent接口:

      public class SampleEvent : Event, ICancellableEvent
      {
          public int MyValue { get; set; }
          public bool IsCancelled { get; set; }
      }

      ——————————————————————————————————

      MyPlugin myPlugin = ...;
      IEventBus eventBus = ...;
      ILogger<xxx> logger = ...;
         
      var @event = new SampleEvent
      {
          MyValue = 20
      };
         
      await m_EventBus.EmitAsync(myPlugin, this /* 发送人 */, @event);
      if(@event.IsCancelled)
      {
          logger.LogInformation($"Event has been cancelled!");
          return;
      }
       
      logger.LogInformation($"Event value: {@event.MyValue}");

      当你的插件卸载或服务被释放时,不要忘记取消订阅C#事件和委托。例如,如果要在加载插件时从Unturned订阅onEnemyConnected事件,还必须按以下方式取消订阅:

      public async UniTask OnLoadAsync()
      {
          Provider.onEnemyConnected += OnPlayerConnected;
      }
       
      public async UniTask OnUnloadAsync()
      {
          // 这是非常重要的,否则你的插件将无法正确支持重新加载和卸载。
          Provider.onEnemyConnected -= OnPlayerConnected;
      }

      避免编写单例事件侦听器。如果事件侦听器具有临时或作用域依赖项,则这可能会导致问题。

      请登录之后再进行评论

      登录
      极客之爱
      个人签名:收容失效Q群: 1037114248
      关注3 粉丝3 喜欢1内容14
      上海
    • 做任务
    • 实时动态
    • 签到中心
      小黑屋
    • 到底部