Fractal Softworks Forum

Please login or register.

Login with username, password and session length

Author Topic: Pooling for (or listening to) events, that occur on bottom left.  (Read 1656 times)

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile

Hello!

I am trying to find a way to get feed of events, that occur on bottom left side of screen.

At first i hooked up "CampaignEventListener", but from what i gathered, it does not have events i need.

Then i've stumbled upon "RaidIntel" (http://www.fractalsoftworks.com/starfarer.api/index.html?com/fs/starfarer/api/impl/campaign/intel/raid/RaidIntel.html). I am more interested in raids and inspections.
Since it implements "IntelInfoPlugin", i thought i could pool for it. So i implemented my own "EveryFrameScript" to pool for new plugins in "Global.getSector().getGenericPlugins()", so i could check if any intel module appears in there, yet nothing appears.

Maybe someone had stumbled into this before and knows where to find it?
To be more precise on my goals:
I need to pool (or listen) for new game events that occur in game on bottom left screen. (Raid, invasion, new bounty, new mission, INSPECTION)
Logged

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24125
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #1 on: July 13, 2020, 12:22:03 PM »

Hi! There's no notification for these events, so your best bet would be to periodically check Global.getSector().getIntelManager().getIntel() for changes.
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #2 on: July 14, 2020, 02:58:08 AM »

Thank you!

I've managed to do it, but i've encountered another issue.

I've implemented "EveryFrameScript" in my custom class and then add it via
"Global.getSector().addScript(...);"
or
"Global.getSector().addTransientScript(...);"

But i can't store anything in object fields. Are objects recreated each time and only class type is stored?

E.g.
Code
public class TestEveryFrameScript implements EveryFrameScript {

long timestamp = Long.MIN_VALUE;

public TestEveryFrameScript()
{
timestamp = Global.getSector().getClock().getTimestamp();
}

        // other mandatory method implementations
}
Field "timestamp" is always 0, on every "advance" call.

I want to store timestamps, so the code in "advance" wouldn't be executed every frame. Are there any workarounds or maybe i should simply slap on "static" and be done with it?
« Last Edit: July 14, 2020, 02:59:49 AM by Kolokol »
Logged

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24125
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #3 on: July 14, 2020, 08:29:18 AM »

By "each time" do you mean "each frame"? No, definitely not. You should be able to store data in fields normally like you'd expect. If that doesn't appear to be working, then my guess is you're missing something, that is, some other kind of bug in your code.

Definitely don't make it static. That will likely lead to save-game related issues, since in that case the variable's value will persist across different saves within the same application session.
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #4 on: July 14, 2020, 09:57:26 AM »

Edit: Removed previous post text. After some debugging i noticed that non static fields retain their value if set in "advance(...)" method. If i set them in constructor, they will have default values, when next time accessed in "advance(...)". I still have suspicion that some object cloning is at place and values are not copied :D But maybe its some JAVA niuanse i am not familiar with.

Is it okay to use network socket to send data from addon to another application? Game doesn't like me using java.io.* package but found a working workaround, wonder if it's not something that is going to be patched in future.
« Last Edit: July 14, 2020, 03:46:20 PM by Kolokol »
Logged

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24125
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #5 on: July 15, 2020, 10:16:12 AM »

Edit: Removed previous post text. After some debugging i noticed that non static fields retain their value if set in "advance(...)" method. If i set them in constructor, they will have default values, when next time accessed in "advance(...)". I still have suspicion that some object cloning is at place and values are not copied :D But maybe its some JAVA niuanse i am not familiar with.

Hmm, that's strange. The base game sets some values for various scripts in their constructors, and that works fine.

Do you mean after a save/load cycle, or just immediately? And is there any chance you might, say, be calling a different constructor than the one you're looking at? I'm somewhere around 99.9% sure this is "user error" of some sort :)

One thing to note: when a savegame is loaded, the default constructor for an object is *not* going to be called as it's re-instantiated. That's only called when you explicitly create the object.


Is it okay to use network socket to send data from addon to another application? Game doesn't like me using java.io.* package but found a working workaround, wonder if it's not something that is going to be patched in future.

That's mostly to stop someone without too much experience from shooting themselves in the foot doing file i/o etc. So, that should be fine. It might (accidentally) end up getting patched out, but that doesn't seem super likely.
Logged

SafariJohn

  • Admiral
  • *****
  • Posts: 3023
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #6 on: July 15, 2020, 12:47:34 PM »

I want to store timestamps, so the code in "advance" wouldn't be executed every frame. Are there any workarounds or maybe i should simply slap on "static" and be done with it?

You want IntervalUtil. Lots of vanilla EveryFrameScripts use it, so just copy them.
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #7 on: July 16, 2020, 01:21:30 AM »

You want IntervalUtil. Lots of vanilla EveryFrameScripts use it, so just copy them.

Thanks! Will check it out latter! I've already been using "Global.getSector().getClock()", it has all i need for now.

Hmm, that's strange. The base game sets some values for various scripts in their constructors, and that works fine.

Do you mean after a save/load cycle, or just immediately? And is there any chance you might, say, be calling a different constructor than the one you're looking at? I'm somewhere around 99.9% sure this is "user error" of some sort :)

One thing to note: when a savegame is loaded, the default constructor for an object is *not* going to be called as it's re-instantiated. That's only called when you explicitly create the object.

I see, that might be the issue. I think i attach EveryFrameScript at the wrong time in lifecycle.

Code
@Override
public void onGameLoad(boolean newGame)
{
SectorAPI sectorAPI = Global.getSector();
if (sectorAPI != null)
{
socketTest = new SocketTest();
TestEveryFrameScript testEveryFrameScript = new TestEveryFrameScript(socketTest);
sectorAPI.addScript(testEveryFrameScript);
}
}

Edit: Damn, i do not know how. But today, when debugging, all object fields seem to persist. I have no idea how so, but ill continue to check it from time to time.

Edit 2: Is okay to fire off additional thread in mod? Need it to pass data via socket, don't want to clog main pipeline. Haven't tested if script launcher allows that.
« Last Edit: July 16, 2020, 08:39:18 AM by Kolokol »
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #8 on: July 19, 2020, 03:47:09 PM »

So i finally figured the issue with the object fields after some heavy debugging.

If i create EveryFrameScript with my custom fields, constructor will set them at the first run.
Then i save the game.
If i add brand new object field to existing class and add initialization in constructor, it will not take effect as soon as i load the game, that already uses the addon. Old fields will have their values whilst new one will not. (As the post above mentioned objects being loaded from a savefile)

But i do wonder - how other modders go about it? If they need to update something in addon that's already being used? Is there a way to invalidate stored object instances?
« Last Edit: July 19, 2020, 03:50:14 PM by Kolokol »
Logged

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24125
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #9 on: July 19, 2020, 03:50:03 PM »

protected Object readResolve() {
    if (field == null) {
        // initialize field
    }
    return this;
}

This method gets called when an object is read from the savefile. This is the approach I generally use to make saves compatible going forward while I'm working on something.
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #10 on: July 20, 2020, 03:10:05 AM »

Thank you! It worked like a charm.
Tho the general behaviour looks interesting. "readResolve()" fires first, and then class constructor is called and "readResolve()" set value is null, but whenever "advance()" fires it already has the proper value from "readResolve()".

Didn't configure Eclipse properly yet, so going out of the scope of callee displays class not found error, so i can't track lifecycle properly :D

If i set static fields, they will not be saved in save state, right?
My mod itself does not keep any useful game data, just implements CampaignEventListener and EveryFrameScript to pool/listen for events and store them in a separate message stack, which then gets dumped over to separate application via socket.
Logged

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24125
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #11 on: July 21, 2020, 09:36:57 AM »

Tho the general behaviour looks interesting. "readResolve()" fires first, and then class constructor is called and "readResolve()" set value is null, but whenever "advance()" fires it already has the proper value from "readResolve()".

I thought you were saying the default constructor wasn't getting called at all on load, which is why you needed something like readResolve() in the first place?

If i set static fields, they will not be saved in save state, right?

Correct. But: don't use static fields to put any data into! They will cause a giant memory leak when another game is loaded, since they'll still point at the previous game's data. Even if you think you might have some way of handling it, really, just don't :)
Logged

Kolokol

  • Ensign
  • *
  • Posts: 13
    • View Profile
Re: Pooling for (or listening to) events, that occur on bottom left.
« Reply #12 on: July 21, 2020, 02:00:21 PM »

I thought you were saying the default constructor wasn't getting called at all on load, which is why you needed something like readResolve() in the first place?

No no. It was getting called, but i did not understand why values where not being set. But now i understand.
First time object was created without any class fields and the game was saved.
On my next run, i added some fields to the class and run the debugger and was confused, as to why fields had no values on "active()", even tho constructor was clearly setting them.
Now i understand that the object from the first run, that had no class fields, was being loaded and that's why fields where null, even tho class constructor set them.
I had it tested by adding additional fields to class. After load old fields where set and new ones where null (even tho constructor was setting them).

But what confuses me a little is that object is restored and used, but the new object is also created and added to "sectorAPI.addScript(testEveryFrameScript);" yet it never fires or doesn't fire twice.

Also another thing that confuses me - if i create a different constructor, that sets additional data, that data won't be set for an already loaded object from a safe file as it will ignore whatever constructor does.

Correct. But: don't use static fields to put any data into! They will cause a giant memory leak when another game is loaded, since they'll still point at the previous game's data. Even if you think you might have some way of handling it, really, just don't :)

I am not storing the game data, i am storing reference to a custom message stack that is used to send data over socket, saving it has no value, as it gets cleaned as soon as data is sent over. But from what i understand from your post, that it's okay to use it as long as i don't use it to store game state related data.


Thank you very much for your responses!
I am a little on vacation right now, i plan to finish up my mod and show it to the world afterwards, maybe then it will be clearer what my goals are :D
« Last Edit: July 21, 2020, 02:20:18 PM by Kolokol »
Logged