psiberx / cp2077-codeware

Cyberpunk 2077 library and framework for creating script mods.
MIT License
75 stars 11 forks source link

Cannot create entity with DynamicEntitySystem from a callback #15

Closed poirierlouis closed 7 months ago

poirierlouis commented 9 months ago

I'm creating a NPC with DynamicEntitySystem and then a workspot entity using Cyberscript Core Archive. To keep things synchronized and consistent I do the following:

  1. Spawn NPC
  2. Get ref to NPC in callback
  3. Spawn workspot (in above callback)
  4. Get ref to workspot in second callback

Issue is that spawning an entity within a callback function doesn't work. It executes from (1) to (3), but callback at (4) is never triggered. I also tried to listen for Entity/Attach and Entity/Attached events from CallbackSystem but it doesn't work either (no trigger).

A workaround I found is to execute (3) in a delay callback. Trying with a delay of 0.1s, it does trigger the second callback as expected. Is calling CreateEntity in a callback function prohibited or can it be fixed somehow?

Here an example that replicate my situation (haven't tried it in this exact form):

public class MySystem extends ScriptableSystem {
  private let m_entitySystem: wref<DynamicEntitySystem>;
  private let m_player: wref<PlayerPuppet>;
  private let m_puppet: wref<NPCPuppet>;
  private let m_workspot: wref<GameObject>;

  public func OnAttach() {
    this.m_entitySystem = GameInstance.GetDynamicEntitySystem();
    this.m_entitySystem.RegisterListener(n"MyMod.Puppet", this, n"OnPuppetUpdate");
    this.m_entitySystem.RegisterListener(n"MyMod.Workspot", this, n"OnWorkspotUpdate");
  }

  // (1)
  public func OnPlayerAttach(request: ref<PlayerAttachRequest>) {
    if GameInstance.GetSystemRequestsHandler().IsPreGame() {
      return;
    }
    this.m_player = GetPlayer(this.GetGameInstance());

    let spec = new DynamicEntitySpec();

    spec.recordID = t"Character.Judy";
    spec.position = this.m_player.GetWorldPosition();
    spec.orientation = this.m_player.GetWorldOrientation();
    spec.tags = [n"MyMod", n"MyMod.Puppet"];
    this.m_entitySystem.CreateEntity(spec);
    LogChannel(n"DEBUG", s"RequestPuppet");
  }

  private cb func OnPuppetUpdate(event: ref<DynamicEntityEvent>) {
    let type = event.GetEventType();
    let id = event.GetEntityID();

    LogChannel(n"DEBUG", s"OnPuppetUpdate(event: \(type))");
    if Equals(type, DynamicEntityEvent.Spawned) {
      // (2)
      this.m_puppet = this.m_entitySystem.GetEntity(id) as NPCPuppet;
      LogChannel(n"DEBUG", s"PuppetReady");
      // (3)
      this.CreateWorkspot();
    }
  }

  private func CreateWorkspot() {
    let spec = new DynamicEntitySpec();

    //spec.templatePath = r"base\\gameplay\\devices\\drop_points\\drop_point.ent";
    spec.templatePath = r"base\\cyberscript\\entity\\workspot_anim.ent";
    spec.position = this.m_player.GetWorldPosition();
    spec.orientation = this.m_player.GetWorldOrientation();
    spec.tags = [n"MyMod", n"MyMod.Workspot"];
    this.m_entitySystem.CreateEntity(spec);
    LogChannel(n"DEBUG", s"RequestWorkspot");
  }

  // (4)
  private cb func OnWorkspotUpdate(event: ref<DynamicEntityEvent>) {
    let type = event.GetEventType();
    let id = event.GetEntityID();

    LogChannel(n"DEBUG", s"OnWorkspotUpdate(event: \(type))");
    if Equals(type, DynamicEntityEvent.Spawned) {
      this.m_workspot = this.m_entitySystem.GetEntity(id) as GameObject;
      LogChannel(n"DEBUG", s"WorkspotReady");
    }
  }
}
psiberx commented 7 months ago

I can't reproduce it on current version.

poirierlouis commented 7 months ago

Alright, I'll try it again. Maybe it is fixed in 2.12a indeed.

psiberx commented 7 months ago

I'll close it for now and reopen if you can reproduce it.