Piotrekol / StreamCompanion

osu! information extractor, ranging from selected map info to live play data
MIT License
369 stars 59 forks source link

Feat: Support obtaining specified data in a low resource consumption manner by specifying the required token. #386

Closed LamGC closed 1 year ago

LamGC commented 1 year ago

This change allows users to request /json endpoints by specifying one or more token query fields, allowing SC to return a small amount of data and obtain data at a low frequency with lower resource usage.

I originally intended to use this method to obtain time at low frequencies for more accurate PlayStatus judgment, but it did not work. But this change looks good.

Piotrekol commented 1 year ago

Is there a specific use case where you are not able to use websocket endpoint? as mentioned in /json docs:

This endpoint should be used only as a tokens reference. Use these via tokens WebSocket endpoint in actual implementations.

I REALLY don't want users to mistakenly start using this endpoint exclusively at high rps instead of WS.

LamGC commented 1 year ago

Is there a specific use case where you are not able to use websocket endpoint? as mentioned in /json docs:

This endpoint should be used only as a tokens reference. Use these via tokens WebSocket endpoint in actual implementations.

I REALLY don't want users to mistakenly start using this endpoint exclusively at high rps instead of WS.

My initial goal in implementing this change was to more accurately identify the current game status (Listening? Playing? Failed?)

So I wrote this code to make a judgment:

function getPlayStatus(status: number, playerHp: number): PlayStatus {
    console.debug("Status: ", status, "HP: ", playHp, "Time: ");
  if (status == 1) {
      return PlayStatus.Listening;
  } else if (status == 2 && playHp > 0) {
      return PlayStatus.Playing;
  } else if (status == 2 && playHp <= 0) {
      return PlayStatus.Failed;
  } else if (status == 32) {
      return PlayStatus.Completed;
  } else if (status == 8) {
      return PlayStatus.Replaying;
  } else {
      return PlayStatus.Unknown;
  }
}

But the problem is that if we rely solely on status and playerHp for judgment, when starting a new game, HP will slowly increase from 0 until it reaches firstHitObjectTime, which will be briefly recognized as Failed and may affect WebUI animation. Therefore, I would like to add the time parameter to determine whether the game is starting or not, rather than failing. (Because when time has not yet reached firstHitObjectTime, time is less than 0.)

And then I attempted to introduce a time token into WebSocket. However, due to unnecessary data tracking, The CPU usage of the browser has increased by 20%. (Because I only use time data when the status changes to check if there is a false failed status due to starting the game, i have an idea to design a failed animation for the WebUI)

tl;dr

What I mean is that we can provide a method to obtain the data of a high-frequency refreshed token at the current time, without using WebSocket or having SC serialize all tokens completely.

I agree that it should be specifically pointed out in the document that data should not be frequently obtained from the /JSON endpoint through polling, but rather WebSocket should be used to frequently receive data updates.

Piotrekol commented 1 year ago

My initial goal in implementing this change was to more accurately identify the current game status (Listening? Playing? Failed?)

This sounds like something that could be added to SC directly instead, subStatus token? (Failed, Completed/Passed, Replaying, Paused, not sure what else could be here)

to determine whether the game is starting or not

..again sounds like something that could be added as a token itself (0/1 value)

And then I attempted to introduce a time token into WebSocket. However, due to unnecessary data tracking, The CPU usage of the browser has increased by 20%.

[...] provide a method to obtain the data of a high-frequency refreshed token at the current time

As watched tokens can be changed during the lifetime of the same WS connection, this could be alleviated by requesting time token only when necessary(in your case: map intro time, and only until hp=0). This could be even done in a separate WS connection that is left with no tokens listening until needed.

LamGC commented 1 year ago

This sounds like something that could be added to SC directly instead, subStatus token? (Failed, Completed/Passed, Replaying, Paused, not sure what else could be here)

At present, the only states that I can determine directly based on status are Listening, Playing, Completed and Replaying.

It would be great if these could be provided directly through SC, but they are beyond my capabilities and I may not be able to help much.

Then there was WebSocket, and I modified the tool class according to your suggestion, and it worked. (Although it is still not possible to accurately determine Failed, as time will not be immediately reset to a negative number when the status changes)

Thank you for your suggestion. It may not be necessary to merge this PR to avoid incorrect usage.