Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace EventDispatcher with an off the shelf solution #1827

Closed
cwisniew opened this issue May 14, 2020 · 18 comments
Closed

Replace EventDispatcher with an off the shelf solution #1827

cwisniew opened this issue May 14, 2020 · 18 comments
Assignees
Labels
code-maintenance Adding/editing javadocs, unit tests, formatting. feature Adding functionality that adds value refactor Refactoring the code for optimal awesomeness.

Comments

@cwisniew
Copy link
Member

Is your feature request related to a problem? Please describe.
The EventDispatcher class should be replaced with an off the shelf event dispatcher.

Describe the solution you'd like
Replace EventDispatcher with Google EventBus.

Additional context
This required replacing current

  • MapTool.ZoneEvent
  • MapTool.PreferencesEvent

Used in

  • ClientMethodHandler
  • MapTool
  • MapToolFrame
  • PreferenceDialog
  • CommandPanel
  • HTMLFrameFactory
  • ImpersonatePanel
  • SelectionPanel
  • WebAppInitiative

MapTool.setCampaign() should be changed to dispatch a new campaign event, replacing the below code

    AssetManager.updateRepositoryList();
    MapTool.getFrame().getCampaignPanel().reset();
    MapTool.getFrame().getGmPanel().reset();
    // overlay vanishes after campaign change
    MapTool.getFrame().getOverlayPanel().removeAllOverlays();
    UserDefinedMacroFunctions.getInstance().loadCampaignLibFunctions();

Eventually this can be expanded to include other events things like advancing initiative etc.

@cwisniew cwisniew added feature Adding functionality that adds value refactor Refactoring the code for optimal awesomeness. code-maintenance Adding/editing javadocs, unit tests, formatting. labels May 14, 2020
@cwisniew cwisniew self-assigned this May 14, 2020
@cwisniew
Copy link
Member Author

Should also replace
ZoneRenderer.updateAfterSelection which currently does

  public void updateAfterSelection() {
    MapTool.getFrame().getSelectionPanel().reset();
    MapTool.getFrame().getImpersonatePanel().resetIfNotImpersonating();
    HTMLFrameFactory.selectedListChanged();
    repaintDebouncer.dispatch();
  }

ModelChangeEvent which is fired off by

  • Grid
  • Zone

Used by

  • UndoPerZone
  • DrawPanelTreeModel
  • HTMLFrameFactory
  • AbstractMacroPanel
  • ImpersonatePanel
  • SelectionPanel
  • InitiativePanel
  • TokenPanel
  • TokenPanelTreeModel
  • ZoneMiniMapPanel
  • ZoneRenderer
  • ZoneView
  • BaseModel
  • Grid
  • ModelChangeListener
  • Zone
  • WebAppInitiative
  • WebTokenInfo

(yeah its a lot)

@Merudo
Copy link
Member

Merudo commented May 14, 2020

Related: the classes CommandPanel and PlayerListModel implement the deprecated Observer interface, which should be replaced by the EventDispatcher.

@cwisniew
Copy link
Member Author

Related: the classes CommandPanel and PlayerListModel implement the deprecated Observer interface, which should be replaced by the EventDispatcher.

MapToolFrame too

@bubblobill
Copy link
Collaborator

Why this particular Off the Shelf solution?

@cwisniew
Copy link
Member Author

cwisniew commented May 15, 2020

Why this particular Off the Shelf solution?

Because its simple(ish), works well, doesn't require external configuration to set up, there is enough documentation on it, and will be enough for out needs.

If you have an alternate suggestion that fits the above, I am willing to consider it.

@bubblobill
Copy link
Collaborator

http://reactivex.io?

@cwisniew
Copy link
Member Author

cwisniew commented May 17, 2020

@bubblobill looking at that it doesn't really seem to fit what we need.
For example I don't want to worry about subscribing to a new token or zone each time one is added, I also don't want to worry about unsubscribing when they are removed.
EventBus works well for this as when a new Token is added everything listing to Token changes will automatically listening to any changes to the new Token.

From the reading I have done you can use RxJava to have a such an EventBus like set up but its more complicated and you then don't really gain much for the extra complication.

@nmeier
Copy link
Contributor

nmeier commented May 18, 2020

Is this a chance to rework how updates go through the full stack?

There's so much explicit dependency coded into the classes, e.g.

ZoneRenderer.modelChanged>MapTool.updateTokenTree, which updates two components explicitly

  /** Update tokenPanelTreeModel and the initiativePanel. */
  public void updateTokenTree() {
    if (tokenPanelTreeModel != null) {
      tokenPanelTreeModel.update();
    }
    if (initiativePanel != null) {
      initiativePanel.update();
    }
  }

and ServerMethodHandler calling methods straight into the UI, then broadcasting

  public void exposePCArea(GUID zoneGUID) {
    ZoneRenderer renderer = MapTool.getFrame().getZoneRenderer(zoneGUID);
    FogUtil.exposePCArea(renderer);
    server
        .getConnection()
        .broadcastCallMethod(
            ClientCommand.COMMAND.exposePCArea.name(), RPCContext.getCurrent().parameters);
  }

One would have to really untangle all that explicit dependency for a cleaner, clearly separated area of responsibility approach. That would also open up a future for a headless server which seems impossible now.

@cwisniew
Copy link
Member Author

Is this a chance to rework how updates go through the full stack?

There's so much explicit dependency coded into the classes, e.g.

ZoneRenderer.modelChanged>MapTool.updateTokenTree, which updates two components explicitly

  /** Update tokenPanelTreeModel and the initiativePanel. */
  public void updateTokenTree() {
    if (tokenPanelTreeModel != null) {
      tokenPanelTreeModel.update();
    }
    if (initiativePanel != null) {
      initiativePanel.update();
    }
  }

At the moment, the initiative panel already listens for Zone Activations and Token Change events via ModelChangeListener. I believe the Author sprinkled in the updateTokenTree() every change they found wasn't updating the tree properly (and I know some of the not updated properly were due to threading issues). Hopefully once I have addressed the above the whole MapToolFrame.updateTokenTree() method can be deleted.

Really it would be nice to redo all of the InitiativePanel but where will I find the time :)

and ServerMethodHandler calling methods straight into the UI, then broadcasting

  public void exposePCArea(GUID zoneGUID) {
    ZoneRenderer renderer = MapTool.getFrame().getZoneRenderer(zoneGUID);
    FogUtil.exposePCArea(renderer);
    server
        .getConnection()
        .broadcastCallMethod(
            ClientCommand.COMMAND.exposePCArea.name(), RPCContext.getCurrent().parameters);
  }

One would have to really untangle all that explicit dependency for a cleaner, clearly separated area of responsibility approach. That would also open up a future for a headless server which seems impossible now.

Eventually yes, but for the first part just the above change though is big enough. Once that is done the next bit would be untangling things more in how the clients/server/UI interact.

@cwisniew cwisniew modified the milestones: 1.8.0, 1.9.0 May 24, 2020
@cwisniew cwisniew removed this from the 1.9.0 milestone Sep 4, 2021
@kwvanderlinde
Copy link
Collaborator

I can take a look at replacing the Observable/Observer stuff as well, maybe even take a crack at detangling updateAfterSelection().

One more thing in addition to what's already been mentioned here: it might be beneficial to also use an event bus instead of ModelChangeListener. A few perks I would expect to see:

  1. Events would be typed instead of being a plain tag + an untyped argument.
  2. Components (e.g., Selected panel) can generally listen for applicable events without having to make sure it's listening to the current zone. And things that affect tokens could be responded to without having to subscribe to each token in a zone (e.g., when a token light changes, ZoneRenderer/ZoneView could flush its lighting/vision caches).
  3. Model types don't have to maintain a list of listeners.

@Phergus Phergus moved this to Todo in MapTool 1.13.0 Nov 16, 2022
@Phergus Phergus moved this from Todo to In Progress in MapTool 1.13.0 Nov 16, 2022
@Phergus
Copy link
Contributor

Phergus commented Nov 16, 2022

You've got my vote.

@cwisniew
Copy link
Member Author

It's got my vote too :)
This was the intention to move everything that makes sense to the EventBus for the reasons you listed above, such as type safety and decoupling. I was replacing things as I changed them and using them for new items. I am certainly not going to complain if others want to help with refactoring :)

@kwvanderlinde
Copy link
Collaborator

Awesome, glad we're all thinking the same thing!

@kwvanderlinde
Copy link
Collaborator

Just discovered this little nugget: google/guava#1630 (comment)

I'm closing this issue because we are no longer going to be making changes to EventBus other than important bug fixes. The reasons we're discouraging it and some alternatives are listed in the EventBus javadoc.

@thelsing
Copy link
Collaborator

So recommendation seem so to be to use DI with rxjava. Last time I looked at java DI frameworks I dagger2 looked best to me.
IMO we have quite a bit other places in the code where DI would be nice.

@kwvanderlinde
Copy link
Collaborator

I do believe the event bus model is still the most appealing for us, particularly for this reason articulated above:

For example I don't want to worry about subscribing to a new token or zone each time one is added, I also don't want to worry about unsubscribing when they are removed.
EventBus works well for this as when a new Token is added everything listing to Token changes will automatically listening to any changes to the new Token.

It's just that the Guava devs have decided the model isn't so good and would rather use something else.

Whatever we move to in the future, we should still be aiming to use the event bus model inside MapTool. For now, Guava's EventBus can fill that role, we just probably don't want to rely on that particular implementation too heavily considering the project's status.

@thelsing
Copy link
Collaborator

thelsing commented Nov 17, 2022

I agree that some kind of bus seems to suit us. But we could also do this with rxjava as written in here or here. (Stolen from guava eventbus javadoc).

I also can live with keeping guava eventbus. We could also use otto or greenrobots eventbus.

Edit: otto is abandoned too.

@kwvanderlinde
Copy link
Collaborator

I'll just close this off as there should not be any change in behaviour. Bugs can be opened as they surface.

@github-project-automation github-project-automation bot moved this from Needs Testing to Merged in MapTool 1.13.0 Jul 31, 2023
@kwvanderlinde kwvanderlinde moved this from Merged to Done in MapTool 1.13.0 Jul 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
code-maintenance Adding/editing javadocs, unit tests, formatting. feature Adding functionality that adds value refactor Refactoring the code for optimal awesomeness.
Projects
Status: Done
Development

No branches or pull requests

7 participants