ReikaKalseki / Reika_Mods_Issues

The issue tracker for all of my mods - RotaryCraft, its addons, ChromatiCraft, and everything else.
46 stars 14 forks source link

Crash with IC2C with ChC #1964

Open OvermindDL1 opened 6 years ago

OvermindDL1 commented 6 years ago

IndustrialCraft 2 Classic (still being worked on to this day with active development by Speiger) fulfills the public IC2 API (since ic2exp is dead), however the internal API is different. As long as the public IC2 API is used then it is a drop-in replacement (with new and different things, in the 'classic' style).

However, loading it with ChC crashes on load:

java.lang.ClassNotFoundException: ic2.core.block.reactor.tileentity.TileEntityNuclearReactorElectric
    at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:191)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at Reika.ChromatiCraft.TileEntity.AOE.Effect.TileEntityEnergyIncrease$EnergyInterface.<init>(TileEntityEnergyIncrease.java:132)
    at Reika.ChromatiCraft.TileEntity.AOE.Effect.TileEntityEnergyIncrease$IC2ReactorInterface.<init>(TileEntityEnergyIncrease.java:303)
    at Reika.ChromatiCraft.TileEntity.AOE.Effect.TileEntityEnergyIncrease$IC2ReactorInterface.<clinit>(TileEntityEnergyIncrease.java:305)
    at Reika.ChromatiCraft.TileEntity.AOE.Effect.TileEntityEnergyIncrease.<clinit>(TileEntityEnergyIncrease.java:56)

It seems that because IC2C is fullfilling the public IC2 API then this call is passing: https://github.com/ReikaKalseki/ChromatiCraft/blob/master/TileEntity/AOE/Effect/TileEntityEnergyIncrease.java#L128

            if (this.getMod() == null || this.getMod().isLoaded()) {

Where getMod is defined at: https://github.com/ReikaKalseki/ChromatiCraft/blob/master/TileEntity/AOE/Effect/TileEntityEnergyIncrease.java#L328-L331

        @Override
        protected ModList getMod() {
            return ModList.IC2;
        }

And then later in that successful condition block at: https://github.com/ReikaKalseki/ChromatiCraft/blob/master/TileEntity/AOE/Effect/TileEntityEnergyIncrease.java#L128-L146

            if (this.getMod() == null || this.getMod().isLoaded()) {
                try {
                    String[] cs = this.getClasses();
                    for (int i = 0; i < cs.length; i++) {
                        Class c = Class.forName(cs[i]);
                        interactions.put(c, this);
                    }
                    this.init();
                    ChromatiCraft.logger.log("Loaded "+this+" for "+this.getMod());
                }
                catch (Exception e) {
                    ChromatiCraft.logger.logError("Could not load "+this+" for "+this.getMod()+":");
                    e.printStackTrace();
                    ReflectiveFailureTracker.instance.logModReflectiveFailure(this.getMod(), e);
                }
            }
            else {
                ChromatiCraft.logger.log("Not loading "+this+" for "+this.getMod()+"; Mod not present.");
            }

It calls this.init(), which is defined at: https://github.com/ReikaKalseki/ChromatiCraft/blob/master/TileEntity/AOE/Effect/TileEntityEnergyIncrease.java#L313-L326

        protected void init() throws Exception {

            Class c = Class.forName("ic2.core.block.reactor.tileentity.TileEntityNuclearReactorElectric");
            /*output = c.getDeclaredField("output");
            output.setAccessible(true);
            Class c2 = Class.forName("ic2.core.block.reactor.tileentity.TileEntityReactorChamberElectric");
            getReactor = c2.getDeclaredMethod("getReactor");
            getReactor.setAccessible(true);
             */
            //processChambers = c.getDeclaredMethod("processChambers");
            //processChambers.setAccessible(true);
            updateTicker = c.getDeclaredField("updateTicker");
            updateTicker.setAccessible(true);
        }

And the Class.forName("ic2.core.block.reactor.tileentity.TileEntityNuclearReactorElectric"); call at the start of it is reaching into a private class of IC2, which does not exist in IC2C (since it is a private class). In IC2 the TileEntityNuclearReactorElectric is actually ic2.core.block.generator.tileentity.TileEntityNuclearReactorElectric and it still has the updateTicker field (on a parent class) and that field is already public so no need to setAccessible(true) on it. Speiger also confirms that you should not try to time-accelerate the Steam version, just the electric, the Steam one is balanced so that if it was accelerated even by 2x then it would likely explode.

Also, in your IC2ReactorAcceleration.java file the "ic2.core.block.reactor.tileentity.TileEntityNuclearReactorElectric" and "ic2.core.block.reactor.tileentity.TileEntityReactorChamberElectric" would also need updating to "ic2.core.block.generator.tileentity.TileEntityNuclearReactorElectric" and "ic2.core.block.generator.tileentity.TileEntityReactorChamberElectric" respectively as well.

Speiger also states that updateTicker is used as this:

if (this.updateTicker++ % this.getTickRate() == 0) {

So if you want to force a tick just set it to 0, otherwise need to compare it to the getTickRate to make sure it doesn't bypass it.

And just for a tidbit of interesting info, IC2C's name for the class is the original name, ic2exp renamed all kinds of classes oddly from the original ic2.

At the very least ChC just needs to not crash when IC2C is loaded with it. ^.^

ReikaKalseki commented 5 years ago

It sounds like you said three or four contradictory things - what exactly is the fix here?

OvermindDL1 commented 5 years ago

I.E. Simplist thing to do is temporarily link in the IC2C dev jar instead of the dead IC2 and see what errors pop up in your IDE. It's available on the GT6 maven via 'ic2:IC2Classic:1.2.1.8:dev', which you can download via a browser if you want it straight at the usual maven path.

ReikaKalseki commented 5 years ago

That is not how my dev environment works; all non-API code interaction is done reflectively by looking at the decompiled "actual" mod jar.

OvermindDL1 commented 5 years ago

The non-dev version is available for download there as well, just don't add the dev classifier to the url. :-)

ReikaKalseki commented 5 years ago

What?

OvermindDL1 commented 5 years ago

I.E. if you want the release obf'd mod itself just leave off the dev classifier, so instead of ic2:IC2Classic:1.2.1.8:dev use ic2:IC2Classic:1.2.1.8, which then acquires https://gregtech.overminddl1.com/ic2/IC2Classic/1.2.1.8/IC2Classic-1.2.1.8.jar instead.

ReikaKalseki commented 5 years ago

You keep going back and forth between proper URLs and random garbage full of colons, and you seem to be completely missing the fact that the conversion between them is neither clear nor known.

OvermindDL1 commented 5 years ago

random garbage full of colons

That's maven declarations, you know the Java Dependency management System that's built in to every single java build system, right down into Eclipse's core and everything else?

and you seem to be completely missing the fact that the conversion between them is neither clear nor known.

It's very well set in the maven spec, but in general you just concat <mavenserver>/<group_with_dots_replaced_with_slashes>/<name>/<version>/<name>-<version>-<classifier>.jar (where the colon format is <group>:<name>:<version> or <group>:<name>:<version>:<classifier>, and you can append .md5 or .sha1 to get the hashes for confirmation as well. Or if you are using gradle or so just ask it to resolve the dependency and it'll print the URL's it found that it exists on in the maven repositories that your project knows about. The mavenserver is the server like the GT6 one, the root URL path, the group is the first part before the first colon (it corresponds to the root namespace of the library), the name is the project name itself, an the version is the jar version. The classifier is a distinguisher, without it as it's optional you get what is the 'main' version of a package, so for an MC mod that's the version that runs in the normal client. deobf or dev are the common classifiers for mods in the MC ecosystem of the non-obfuscated version, so you can run it straight in the development environment whether for just compilation purposes or full linking. And ones like sources is a Standard classifier used by IDE's to auto-acquire sources and javadoc is a Standard classifier for grabbing documentation and so forth.

But the 'random garbage full of colons' is exceedingly a java standard used by just about every Java library nowadays. Gradle/Ant/etc... may have some other longer formats on occasion (ant's is especially verbose for example) but this is the standard format that most take straight-in. It is highly valuable to manage dependencies so a project doesn't enter into vendoring things in or some horrors like that (that's when you get mismatched versions like using MC 1.6 mod API's in a MC 1.7.10 mod, or out in the real world when you depend on a library that's had a critical security update with an identical API that you don't have to worry about updating manually to make sure you are up to date).

Now you cannot always go 'from' the URL back to the 'garbage full of colons' format as that's ambiguous (related to the group specifically), but from the 'garbage full of colons' format to a URL is always and perfectly unambiguous.

OvermindDL1 commented 5 years ago

In essence, whatever build system you use if you add the GT6 server of https://gregtech.overminddl1.com/ as a maven server to the maven server list and use the 'garbage full of colons' format in the dependency list (or whatever format the build system chosen uses, as stated above Ant's normal format is pretty abhorrantly bad as just one old example, there's a multitude of reasons no one uses ant anymore, but it still makes a great example of poor design), then everything is just auto-acquired and used appropriately. If you want an example I have a full gradle build script that I can show you here that by using git submodules to bring in all your mods (there is a LOT of interdependencies between them...) and compile them each into their own jars (I needed dev/deobf versions of your jars and thus works fantastically) then it all 'just works', even with the huge amount of hard dependencies these mods require (It's an impressive amount, even GT6 only has 31 hard dependencies but almost a hundred soft dependencies) it is just clean and works, this is the java standard style and why every modern (last ~15 years or so) build system does it this way.

ReikaKalseki commented 5 years ago

Is there a GitHub repository or something of the code instead?

OvermindDL1 commented 5 years ago

Is there a GitHub repository or something of the code instead?

Sadly nope, it's a fork of IC2 before the IC2Exp version (technically IC2Exp is the fork and IC2C is the original updated codebase) and since IC2Exp is dead then IC2C became the 'de-facto standard' nowadays for 1.7.10 for a variety of reasons. However, since it is IC2's codebase he's not allowed to open source it, only allowed to maintain it (it follows the original IC2 API before IC2Exp broke things). Even getting this dev version took some doing, I'm not sure this copy exists anywhere else unless he started distributed it after it was made. For the obfuscated release version you can grab it from any of its official mirrors.

Speiger commented 4 years ago

To bring this alive again you could have pinged me and asked if I could answer questions. Because there is actually ways to detect classic easily. @ReikaKalseki & @OvermindDL1

ReikaKalseki commented 4 years ago

And what is the cleanest?

Speiger commented 4 years ago

https://github.com/TinyModularThings/IC2Classic/blob/1.7.10/src/main/java/ic2/api/info/IC2Classic.java#L76 You can import ic2cs api if you really want to, but its not nessesary. But thats the ModID unique to identify IC2Classic. Sadly forge has no "optional" with blacklist options. But this would be at least a clean way for you since you can relyable tell the difference between experimental & classic.