Jump to content

Using a dedicated EventSubscriber vs multiple


Cratthorax

Recommended Posts

Good morning,

I'm almost all way through my current mod port, and while I'm on it, I'd thought to improve code were due. In the past my focus wasn't necessarily on making things working for multiplayer, since I would never play MC online anyways, but now I wanna change that, since I really don't want to crash other users games. I also want to make things a bit more "efficient", if that is even what I'm trying to do?

So, per title, I have a multitude of different event subscribers to intercept recipe/advancements code, which all run on server start(on a Forge bus), and are currently running inside my main file, mixed up with a couple of client related eventSubs to cancel player related render stuff. So my first question would be: is it a good idea to mix client/server events up in one .class, or am I supposed to give them dedicated .classes's? I should mention that my main file is registering for modEventBus as well as forgeEventBus.

Is it even worth worrying to lump them up in one file, or does the game not bother at all, as long as I keep it structured?

Edit: oh, and is multithreading with multiple .classes housing evtSubs even a thing in MC, given how low the used up resources by this game are?

Edited by Cratthorax
Link to comment
Share on other sites

2 hours ago, Cratthorax said:

is it a good idea to mix client/server events up in one .class, or am I supposed to give them dedicated .classes's?

no it´s not since you can only subscribe a class to one Dist server or client if you don't enter a dist it will run on both (common)

2 hours ago, Cratthorax said:

Is it even worth worrying to lump them up in one file, or does the game not bother at all, as long as I keep it structured?

the events are called, no matter what class they are in
if you combine several events (which are registered on the same Dist) in one class you may save some RAM in the extreme case 1-2MB (depends on the class size)

I would recommend that you sort the Events/summarize them in classes, that you can handle the code

  • Like 1
Link to comment
Share on other sites

6 hours ago, Luis_ST said:

no it´s not since you can only subscribe a class to one Dist server or client if you don't enter a dist it will run on both (common)

the events are called, no matter what class they are in
if you combine several events (which are registered on the same Dist) in one class you may save some RAM in the extreme case 1-2MB (depends on the class size)

I would recommend that you sort the Events/summarize them in classes, that you can handle the code

Well, you can run a class inside a class dedicated to client. But I take it that's not a good practice. I just wanted to rule out if it's something to worry about or not.

I do practice to sort event .classes into three categories: client, server, and registry. But from a performance pov I couldn't say there really is any notable improvement, no matter how you structure the files. The only golden rule here seems to be, applying a clear distinction between client(render, sound, ui, model etc.) and server side.

Edited by Cratthorax
Link to comment
Share on other sites

Okay, some clarifications:

  • It doesn't matter for performance if you have one class registered to the event bus or multiple. What matters it he amount of event subscribing methods.
  • You should use @EventBusSubscriber with the Dist parameter for dist-only subscribers (e.g. FMLClientSetupEvent or EntityRenderersEvent.RegisterRenderers). This has nothing to do with performance, but it is necessary to avoid crashing the dedicated server.
  • You should not register the same event handler to multiple event buses. Separate your mod event bus and forge event bus methods into separate classes if you use the class/object based registration.
  • 10 hours ago, Cratthorax said:

    Edit: oh, and is multithreading with multiple .classes housing evtSubs even a thing in MC, given how low the used up resources by this game are?

    Minecraft is largely single threaded. The game logic runs on one thread. World generation and chunk loading is threaded, but most of the time you do not need to worry about it at all. One thing to keep in mind that mod loading is multithreaded, so from FMLCommonSetupEvent you cannot interact with non-threadsafe game code (such as Registries or BrewingRecipeRegistry). Most FML / Forge registration methods are safe though.
    Another note is that in single player there is obviously an intergrated server running, which runs on a separate thread. You have to be careful to not reach across logical sides. In general treat single and multiplayer exactly the same. Server and client can only communicate via network packets.

  • Like 1
Link to comment
Share on other sites

Quote

It doesn't matter for performance if you have one class registered to the event bus or multiple. What matters it he amount of event subscribing methods.

Oh, so it does make sense, if I'm using multiple events to check for "similar" conditions, I should make sure I'm trying to achieve it with only one?

Because I would often use multiple events(in one, or across multiple files/.classes) as backup.

Quote

You should use @EventBusSubscriber with the Dist parameter for dist-only subscribers (e.g. FMLClientSetupEvent or EntityRenderersEvent.RegisterRenderers). This has nothing to do with performance, but it is necessary to avoid crashing the dedicated server.

Which leads me to the question, is there a way to list definitive methods/fields/constructors/types, that are only supposed to be used by client vs server? I mean, I do know you are supposed to keep render, sound, ui stuff etc. dedicated to client, but a comprehensive list would ease up things, and stop wasting time with browsing through examples(in Google, and the forge snapshot). 

Quote

You should not register the same event handler to multiple event buses. Separate your mod event bus and forge event bus methods into separate classes if you use the class/object based registration.

Hmmm, I might be wrong. But the source main .class from example mod actually exactly does that. So I had the impression that's not a problem.

 
	@Mod(blabla.MODID)
public class blablaMain{
	    public blablaMain() {
        
        IEventBus modEvtBus = FMLJavaModLoadingContext.get().getModEventBus();
        
        modEvtBus.addListener(this::setup);
        modEvtBus.addListener(this::enqueueIMC);
        modEvtBus.addListener(this::processIMC);
        modEvtBus.addListener(this::doClientStuff);
	        MinecraftForge.EVENT_BUS.register(this);  
    }
	    private void setup(final FMLCommonSetupEvent evt)
    {
        // some preinit code
    }
    
    private void doClientStuff(final FMLClientSetupEvent evt) {
        // do something that can only be done on the client
    }
	    private void enqueueIMC(final InterModEnqueueEvent evt)
    {
        // some example code to dispatch IMC to another mod
    }
	    private void processIMC(final InterModProcessEvent evt)
    {
        // some example code to receive and process InterModComms from other mods
    }
    
    @SubscribeEvent
    public void onServerStarting(FMLServerStartingEvent evt) {
        // do something when the server starts
    }
    
    @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
    public static class RegistryEvents {
        @SubscribeEvent
        public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegEvt) {
            // register a new block here
        }
    } 
  
}

 

Edited by Cratthorax
Link to comment
Share on other sites

1 hour ago, Cratthorax said:

But the source main .class from example mod actually exactly does that.

No, it doesn't.

1 hour ago, Cratthorax said:

Which leads me to the question, is there a way to list definitive methods/fields/constructors/types, that are only supposed to be used by client vs server?

A good start is the @OnlyIn annotation. If it is present on a class/method/field then that element is only present on that distribution. For example the Minecraft class. This means that from common code you cannot use it - or you will crash servers and cause strange bugs on single player.

A general rule is that the server is in control of the game, the client only follows suit. Things like spawning entities, setting blocks, etc. should only be done on the logical server.

  • Like 1
Link to comment
Share on other sites

Quote

A good start is the @OnlyIn annotation. If it is present on a class/method/field then that element is only present on that distribution. For example the Minecraft class. This means that from common code you cannot use it - or you will crash servers and cause strange bugs on single player.

By distribution you mean "the mod you've distributed", or is that java language related? Also, how exactly one defines "common" code, because I'd see that(folder/package) a lot on other mod examples. Is it mend like the German word "gewöhnlich", or more like "common" for anyone to use(server) vs "closed" not for anyone to use(client).

I think I start to comprehend this a bit more. Utilizing this annotation I would crash test my mod on a LAN server world, and if any of my code crashes the server, I would annotate with @Onlyln to prevent that crash(on server), or make my code server compatible. At least that's how I understand your feedback.

If that is the case, why isn't the example mod source distributed with a simple comment, like this:

//use this class/method/field annotation to make sure you aren't crashing the server, if mod is present: @OnlyIn

 

Edited by Cratthorax
Link to comment
Share on other sites

23 hours ago, Cratthorax said:

By distribution you mean "the mod you've distributed", or is that java language related?

I mean the game distribution (client vs. dedicated server). See also the documentation on sides.

23 hours ago, Cratthorax said:

Also, how exactly one defines "common" code, because I'd see that(folder/package) a lot on other mod examples. Is it mend like the German word "gewöhnlich", or more like "common" for anyone to use(server) vs "closed" not for anyone to use(client).

Common here means used by both server and client.

On 11/14/2021 at 10:36 AM, Cratthorax said:

I think I start to comprehend this a bit more. Utilizing this annotation I would crash test my mod on a LAN server world, and if any of my code crashes the server, I would annotate with @Onlyln to prevent that crash(on server), or make my code server compatible. At least that's how I understand your feedback.

No, do not use @OnlyIn, ever. @OnlyIn is a marker annotation that Forge puts in vanilla code. When Forge sets up the development environment it takes the server jar and the client jar and merges them together. Code that is present only in either one of them gets annotated with @OnlyIn. This tells you that you cannot use said code from common code (i.e. code that runs on server and client), because then you'll run into crashes on the side that does not have the class/method/field available. Refer to the documentation above for how to safely interact with this code (Mainly DistExecutor and @EventBusSubscriber with the Dist parameter).

Link to comment
Share on other sites

No, do not use @OnlyIn, ever. @OnlyIn is a marker annotation that Forge puts in vanilla code. When Forge sets up the development environment it takes the server jar and the client jar and merges them together. Code that is present only in either one of them gets annotated with @OnlyIn. This tells you that you cannot use said code from common code (i.e. code that runs on server and client), because then you'll run into crashes on the side that does not have the class/method/field available. Refer to the documentation above for how to safely interact with this code (Mainly DistExecutor and @EventBusSubscriber with the Dist parameter).

Oh, so you marked any of critical classes/events etc., or is it for just some that can't be used?

Is there any "codeable" way of "stress" testing the /minecraftforge source for crash prone code? Like something that clearly debugs for me which code to not use commonly?

Link to comment
Share on other sites

1 hour ago, Cratthorax said:

Oh, so you marked any of critical classes/events etc., or is it for just some that can't be used

It is an automatic process when setting up the development environment. The server and client code shipped by Mojang is largely identical, but some code is only present on either side. For example the entire texture and rendering system is obviously not present in a server jar. When setting up the development environment these two codebases are merged and differences are annotated with @OnlyIn. This is not done manually.

1 hour ago, Cratthorax said:

Is there any "codeable" way of "stress" testing the /minecraftforge source for crash prone code? Like something that clearly debugs for me which code to not use commonly?

Not that I know of, but I am not 100% sure what your question is.

Link to comment
Share on other sites

Quote

Not that I know of, but I am not 100% sure what your question is.

Well, something like an "idiot" break. Something that allows me using debugging code, that, for example, scans the (any)source for client only annotations etc., and/or tells me: "Hello, you right now utilize client code commonly. Please stop doing that!", as in:

if ((client only)annotation found on class/method/field || any other method to determine client code) {
	                     system.out.println("Hello, you right now utilize client code commonly. Please stop doing that!)
	}
Edited by Cratthorax
Link to comment
Share on other sites

Good morning,

one thing kept me busy thinking though, when you said:"What matters it he(is the?) amount of event subscribing methods.". Is there something like a recommended quantity of subscribed events for any range of PC specs? Something like an absolute "upper limit"?

My mod is quite huge, and it's hard to unite some of its events under one event method.

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.