[Code Analysis] Why is my fission reactor not frying?[Mek] General Machine (Mekanism) Minecraft Game

This tutorial is set by the author to use the CC By-NC-SA protocol.

Looking at the title, I know that I want to analyze the code to introduce the explosive logic of the cracking pile, but it does not rule out that there will be something else, it is not good.

The code comes from an open source warehouse on GitHub. The code logic used has not changed from 1.16.x to 1.19.x, but I do not guarantee that the subsequent versions are the same.

Code logic is mainly from mekanism.Generators.Common.Content.Fission.FissionReactiBlockData.java, which is later referred to as this code.Why is there no internal code block in MCMOD?

The article is inspired by this tutorial, thank you for your research.

For the convenience of reading, meaningful conclusions are labeled with light blue.

0x00 constant definition

 Inverse_INSUlation_Coefficient: Double = 10000

INVERSE_CONDUCTION_COEFFINT: double = 10

WaterConductivity: double = 0.5

COLANT_PER_VOLUME: int = 100000

Head_cooland_per_volume: Long = 1000000

Fuel_per_assembly: Long = 8000

Min_damage_tempeite: double = 1200

Max_damage_temperanture: double = 1800

Max_damage: double = 100

Explosion_Chance: Double = 1/512000

You should not know what the most things inside are, it doesn't matter, we will use it later.

0x01 explosion time when?

The first thing we need to care about is to adjust the explosion from what code, so as to analyze the actual code logic.

Explosion_chance is a very interesting constant. In this code, it is found that there are only two uses. The other is the function Void CreateMeltdown (Level World):

 Private Void CreateMeltdown (Level WORLD) {

RadiantManager.Instance.createMeltdown (world, getminpos (), getmaxpos (), headcapacitor.getheat (), exploss_chance

MekanismgeneratorSconfig.GENERATORS.FISSIONMELTDOWNRADIUS.GET (), Inventoryid);

}

It can be considered that this is our goal. This function is adjusted by another function Handledamage. This will be the core of us afterwards. Handledamage is adjusted by the function tick.

Obviously, handledamage is a key code to control the explosion. At the same time, we will use the explosion to better express "melting" (that is, Meltdown, different from Explosion).

0x02 handledamage

Post the code first:

 Private void handledamage (level world) {

Double lastDamage = reactordamage;

Double test = headcapacitor.gettemperature ();

If (temp> min_damage_temperator) {

Double damagerate = math.min (temp, max_damage_temperator)/(min_damage_temperanture*10);

Reactordamage+= damagerate;

} Else {

Double repairrate = (min_damage_temperature-test)/(min_damage_temperator*100);

Reactordamage = math.max (0, ReactordaMage-Repairrate);

}

// Consider a Meltdown only if we're passed the damage threshold and the testure is Still Dangerous if (ReactOrDamage> = max_damage &&TMP > = Min_damage_temperator) {

If (Isforcedisabled () && MekanismgenToreorsconfig.GERATORS.FISSIONMELTDONSENSENABLED.GET ()) {) {

// If we have meltdownS enabled, and we will have had one beface, but they we disabled, just meltdown ImmediaTelyly

// If Still Meet the requirements for a Meltdown

Setforcedisable (false);

CreateMeltdown (World);

} Else if (world.random.nextD should allow () <(reactiveAmage/max_damage)*mekanismgenratorsconfig.Generators.FissionMeltdownChaance.get ()) {) {) {) {

// Otherwise, if our channel is hit Either Create a Meltdown if it is enabled in the config, or force disable the reactor

If (mekanismgenratorSconfig.GENERATORS.FISSIONMELTDOWNSENABLED.GET ()) {createmltdown (world);

} Else {

Setforcedisable (true);

}

}

} Else If (ReactordaMage

// If are at a saving temperature and damage level, Allow Enability the Reactor Again

Setforcedisable (false);

}

If (ReactordaMage! = LastDamage) {

MarkDirty ();

}

}

Let's dismantle it.

 Double LastDamage = Reactordamage;

double test = headcapacitor.gettemperature ();

if (temp> min_damage_temperator) {

Double damagerate = math.min (temp, max_damage_temperator)/(min_damage_temperanture*10);

Reactordamage+= damagerate;

} Else {

Double Repairrate = (min_damage_temperator-test)/(min_damage_temperanture*100); reactorDamage = math.max (0, reaCTORDAGE-Repairrate);

}

LastDamage's function is different from this small piece of code, but it is not easy to split, let's talk about it later.

TEMP is the current heat of the reactor. If the temperature is greater than a temperature threshold, it will calculate a damage value based on some algorithm, otherwise it will be calculated0).

Since there is a bunch of constants in the calculation formula, we will calculate the constant.

Temperature threshold: 1200

Damage value: Min (TEMP, 1800) / 12000

Repair value: (1200 -TEMP) / 120000

In other words, at least the damage value of 0.1 Per Tick will cause the damage value of the 1/12000 PER TICK every 1K temperature until it reaches 0.15 per two.

The repair value increased with the temperature decreased, and the repair value of 1/120000 PER TICK was increased at a temperature of 1/120000 per 1k until it finally decreased to 0.0075 per two (that is, 300K, this is the theoretical normal temperature).

Note that the values ​​in this are the front number of the percentage displayed by the damage value.

 if (reactorDamage> = max_damage && && & & &_damage_temperant) {

If (Isforcedisabled () && MekanismgenToreorsconfig.GERATORS.FISSIONMELTDONSENSENABLED.GET ()) {) {

// If we have meltdownS enabled, and we will have had one beface, but they we disabled, just meltdown ImmediaTelyly

// If Still Meet the requirements for a Meltdown

Setforcedisable (false);

CreateMeltdown (World);

} Else if (world.random.nextD should allow () <(reactordamage/max_damage)*mekanismgenratorsconfig.Generators.FissionMeltdownChaance.get ()) {// OT Herwise, if our channel is hit either create a meltdown if it is enabled in the config,,,,or force disable the reactor

If (mekanismgeneratorSconfig.GENERTORS.FISSIONMELTDONSENSABLED.GET ()) {) {) {) {) {) {

CreateMeltdown (World);

} Else {

Setforcedisable (true);

}

}

}

Among them are the Getter/Setter function of Boolean variable forcedisable for the Getter/Setter function.

@containersync

Private Boolean Forcedisable;

void SetforceDisable (Boolean FORCEDISABLE) {{

If (this.forcedisable! = Forcedisable) {

This.Forcedisable = Forcedisable;

MarkDirty ();

If (this.forcedisable) {

// If are force disabling it, deactive the reactor

SetActive (false);}

}

}

@ComputerMethod

public boolean isforcedisabled () {) {

Return forcedisable;

}

MekanismGENERATORSCONFIG.GENERTORS.FISSIONMELTDOWNSENABLED.GET () Return to whether it is enabled in the configuration file, and defaults to TRUE.

MekanismgeneratorSconfig.Generators.FissionMeltdownChaance.get () is the probability of reacting the melting in the configuration file. This value is 0.001 by default.

The first is a judgment. If the damage value exceeds the maximum damage value and the temperature exceeds the damage threshold, a series of logic is performed. This condition is called the melting condition.

Mission condition: temperature ≥ 1200K and damage value ≥ 100

Look at the logic of the second branch in the code block.A random decimal from 0 to 1. If the random number is less than the existing damage value, the maximum damage value is then multiplied by the melting probability, then enter a judgment: if the melting is enabled, the melting occurs, otherwise the forcedisable is recorded as true as true(At the same time, it will cause the reactor for compulsory shutdown).Calculate the constant.

Mission probability = (damage value / 100) * Configure the melting probability => damage value * 0.00001, detect each Tick once.

For example, when the damage value is 1000%, the melting probability is 1%. At this time, the probability of 81.8% of the reactor is not melted in one second.At this time, it is recommended to disassemble the reactor and rebuild.

Let's look at the first branch again.This actually occurs when you meet the melting conditions of the reactor and the melting judgment is successful, and the configuration has not been allowed to be melted to allow melting.

 else if (reactorDamage 

// If are at a saving temperature and damage level, Allow Enability the Reactor Again

Setforcedisable (false);

}

If the reactor does not meet the melting conditions, the forcedisable is changed to false.This shows that if the reactor meets the melting conditions, then returns to the safe damage value, and then opens the melting in the configuration, and then the melting conditions will not be melted immediately when the melting conditions are triggered.Of course, if you have a bad luck, you just encounter the probability of melting, then I didn’t say

 if (reactorDamage! = LastDamage) {

MarkDirty ();

}

Update damage value data.

We can draw some conclusions.

Regardless of whether the reactor is running, as long as it meets the melting conditions and the melting is turned on in the configuration file, it may melt.

Regardless of whether the reactor is running, as long as the temperature exceeds 1200K, the reactor will definitely accumulate the damage value.

The recovery of the damage value is affected by the temperature and very slow, so it is more efficient to reduce the damage value to 0 to 0 for a while, and it is best not to cause the damage value.

It is effective to avoid melting from melting, but do not turn on the melting switch before leaving the melting conditions.What are you doing when melting? Is there any vacancy? Can you come to save it?

We only know how the explosive conditions cannot meet us. We also want to know how the CreateMeltdown function works.

 Private void CreateMeltdown (level world) {

RadiantManager.Instance.createMeltdown (world, getminpos (), getmaxpos (), headcapacitor.getheat (), exploss_chance

MekanismgeneratorSconfig.GENERATORS.FISSIONMELTDOWNRADIUS.GET (), Inventoryid);

}

The parameters are the boundary of the multi -block structure (that is, the reactor).

Continue to track the call function.

 Public Void CreateMeltdown (Level World, Blockpos Minpos, Blockpos Maxpos, Double Magnitude, Double CHANCE, FLOAT Radius, UUID Multiblockid) {

MELTDOWNS.COMPUTEIFABSENT (world.dimension (). Location (), ID-> New ArrayList <> ()). Add (New Meltdown (minpos, maxpos, Magnitude, CHANCE, RADIUS,, Radius,, Radius, Radius, Multiblockid);

MarkDirty ();

}

Among them, Meltdowns is a map >.

This code is to store a Meltdown with a melting information into the list of positions. Let's see where there will be actual processing of Meltdowns.

 Public Void TickServerworld (Level World) {

Elastic

// Update Meltdowns

List DimensionMeltdowns = Meltdowns.GetOnterfault (world.dimension (). LOCATION (), CollectionS.emptyList ());

If (! DimensionMeltdowns.isempty ()) {{) {) {) {

DimensionMeltdowns.removeif (Meltdown-> Meltdown.Update (World)); // If we have/had any metapns mark our data handler as meltdown updates

// The Number of Ticks It has been around for Will Change

MarkDirty ();

}

}

It seems that each tick will execute the Update for a Meltdown and determine whether to continue this Meltdown based on the return value, so let's see how Meltdown is written.

 Public Boolean update (level world) {{

Ticksexisted ++;

If (world.random.nextint ()%10 == 0 && world.random.nextdouble ()

Int x = mth.nextint (world.random, minpos.getx (), maxpos.getx ());

Int y = mth.nextint (world.random, minpos.get (), maxpos.get ());

Int Z = mth.nextint (world.random, minpos.getz (), maxpos.getz ());

CreateExplosion (World, X, Y, Z, Radius, TRUE, Explosion.blockInteraction.destroy);

}

If (! WorldUtils.isBlockloadloaded (world, minpos) ||! WorldUtils.isblockloaded (world, maxpos)) {) {) {) {

Return true;}

Return tickSexisSEd> = Duration;

}

Duration is a constant 100, and Magnitude is the temperature of the reactor during melting.

It can be found that this function calls up to 100 times, and then returns True to remove itself out of MeltdownS. This function will not be called. At this time, we say that this Meltdown is invalid.

Every 10 Ticks, a random judgment occurs. This probability is a very simple temperature multiplier configuration probability.Through simple theory analysis, we will find that this judgment will definitely call it before this Meltdown fails.If the judgment is successful, a position that can destroy blocks will be generated in a multi -square structure.

Configuration probability is a constant, so.Essence

Explosion probability = Reactor temperature / 512000 during melting

Because the temperature of the reactor at least 1200 during melting, the probability of the explosion was at least 0.00234375, but in this case, the probability that the explosion did not occur was 97.68%.

 Private Void CreateExplosion (level world, double x, double y, double z, float Radius, boolean causesfire, exploss.b Lockinteraction Mode) {

Elastic

// Next Go Through The Different Locations that We Inside Our Reactor That Shior Exploded and Make Sure

// that if they do, expliced ​​that we manually run the logic to make them.

// Note: Shuffle so that the drops do end end up all in one corner of an explosion

Util.shuffle (TOBLOW, World.random);

List > Drops = New ArrayList <();

For (blockpos toExplode: TOBLOW) {blockstate state = world.getBlockState (toexplode);

// if the block did'T almedy get brouren when running the normal explosion

If (! State.isair ()) {{

If (State.candropFromexplove (World, TOEXPLODE, Explosion) && World Instanceof Serverlevel Level) {

Blockity tilentity = state.hasblocktity ()? World.GetBlockIntity (toexplode): null;

LootContext.Builder LootContextBuilder = New LootContext.builder (Level)

.Withrandom (world.random)

.Withparameter (LOOTCONTEXTPARAMS.ORIGIN, VEC3.ATCENTEROF (Toexplode))

.Withparameter (LOOTCONTEXTPARAMS.TOOL, Itemstack.empty)

.WithoptionalParameter (LOOTCONTEXTPARAMS.BLOCK_ENTITY, Tilentity). WithoptionalParameter (LOOTCONTEXTEXTPARAMS.This_entity, NULL), null); null); null); null); null); null); null); null); null); null); null);

If (mode == Explosion.blockInteraction.destroy) {

LootContextBuilder.withparameter (LOOTCONTEXTPARAMS.EXPLOSION_RADIUS, RADIUS);

}

State.getdrops (LootContextBuilder). Foreach (Stack-> addBlockdrops (Drops, Stack, TOEXPLODE));

}

State.onblockexploded (world, toExplode, Explosion);

}

}

For (Pair Pair: Drops) {

Block.popRsource (world, pair.getSecond (), pair.getfirst ());

}

}

After the explosion occurs, if the reactor is not frying, it will also take off the reactor intimately (see annotations).

Because even if the melting occurs, there is a considerable probability that the reactor does not explode, so we better calculate the probability of explosion.

One explosion probability = temperature / 512000

Ten times without explosion probability = (1 -explosion probability) ^ 10 The probability of damage to the reactor = melting probability* (1 -non -explosive probability of ten times) = (damage value/ 100)* Configuration melting probability* (1-The temperature/ 512000) ^ 10 => damage value * 0.00001 * (1 -temperature/ 512000) ^ 10

Note that this probability should also be considered as judged by each Tick, because multiple melting in the same melting conditions will be considered as multiple Meltdown for judging.

Under the minimum melting conditions, the probability of reactor damage per TICK is 0.0977%, which means that the probability of reactor can live by 30 seconds by 55.63%, and the probability of living one minute is 30.95%.

Even if the reactor returns to the non -melting conditions, it does not mean that it is safe, because there is a 5 -second judgment of a melt at a time, and the melting may be triggered before entering the non -melting conditions.Then bombed the reactor.

0x04 cooling orange warning ... I'm sorry, get the wrong manuscript

It is only the last step to understand the melting related mechanism completely, that is, how the temperature changes.

The temperature is determined by heat, so we first consider how the heat determines the temperature.

The temperature/heat management class used in the reactor is variableHeheatcapaCitor. The GetTeMPATURE function of this class inherits the father's BasicheatCapaCitor. It is achieved:

Temperature = calories / heat capacity

The heat capacity is given by the reactor:

 headcapacitor.setHetheatCapAcity (MekanismGeneratorsConfig.Generators.fissionCasingheatCapAcity.get ()*local ), True); 

That is, the number of blocks of the block structure of the heat capacity is multiplied by the CasingheatcapaCity configuration in the configuration.

Hot capacity = number of shell block * 1000 = (length * width * height- (long -2) * (width -2) * (height -2)) * 1 000

In Mekanism, the heat change is treated by the function Handleheat. This function is called four times in this code, and we analyze it separately.

@outerride

public double simulatingenvalonment () {) {) {)

Double infonduction = headapi.air_inverse_Coefficient+(INVERSE_INSULATION_COEFFICIENT+Inverse_Conduction_COEFFICient)

Double Temptotransfer = (Heatcapacitor.getTemperature () -BiomeambientTemp)/InvConduction;

Heatcapacitor.handleheat (-temptotransfer*headcapacitor.getheatCapAcity ());

Return math.max (temptotransfer, 0);

}

Environmental temperature, this function is called once every TICK, and according to the current temperature and environmental temperature, the current temperature is gradually adjusted to the ambient temperature.The smaller the difference between the two, the smaller the adjustment.

Adjustment amplitude = | (reactor temperature -ambient temperature) / 20010 |, the absolute value is because the discussion is not good -looking, so it is changed to a "amplitude" without positive sign.<<<<<<<] = "assh: java;">> prize

Double test = headcapacitor.gettemperature ();

Double heat = getBoileFFICIENCY ()*(temp-heatutils.Base_Boil_temp)*headcapacitor.getheatCapAcity ();

Long coolantheated = 0;

If (! FLUIDCOLANTTANK.ISEMPTY ()) {

Double caseheat = head*waterConductivity;

COLANTHEATED = (int) (Heatutils.getsteamenerGyefficience ()*CaseCoolandheat/Heatutils.GetWatermalenthalpy ());

COLANTHEATED = mth.clamp (Coolandhead, 0, FluidCoolantank.Getfluidamount ()););););););););););

If (COOLANTHEATED> 0) {

Mekanismutils.logmismatcheDStacksize (FluidCoolandtank.shrinkStack ((int) Coolandheated, Action.execute), COOLANTHEATED); COOLANTHEATED);

// Extra Steam is dumped

Headcoolantank.InSERT (mekanismgases.team.getstack (Coolandheated), Action.execute, AutomationType.internal);

CaseCoolanTheat = Coolandheated*Heatutils.GetWaterMalenthalpy ()/Heatutils.getsteameFFICICIENCY (); HeatCapacitor.handleheat ( -CaseCoolandheat);

}

} Else if (! Gascooelanttank.isempty ()) {) {

CooledCoolanTType = Gascoolandtank.getStack (). Get (cooledCooland.class);

If (Coolandtype! = NULL) {

Double caseheat = head*coolnttype.getConductivity ();

COLANTHEATED = (int) (CaseCoolandheat/CoolandType.getthermalenthalpy ());

COLANTHEATED = mth.clamp (Coolandhead, 0, Gascoolandtank.GetStored ());

If (COOLANTHEATED> 0) {

Mekanismutils.logMISMATCHEDSTACKSIZE (Gascoolanttank.shrinkStack ((int) Coolandheated, Action.execute), CoolanTheated);

HeatedCoolantank.INSERT (CoolandType.getheatedgas (). GetStack (Coolandhead), Action.execute, AutomationTypernal); CaseCool) Antheat = coolantheated*coolnttype.gettthermalenthalpy ();

Headcapacitor.handleheat (-casecoolandheat);

}

}

}

LastBoilrate = coolntheated;

}

The cooling agent was controlled, and the function getBoilefficience was called.

@ComputerMethod

Public double getBoilefficience () {) {)

Double avgsurfacearea = (double) Surfacearea/(double) fulassemblies;

Return math.min (1, Avgsurfacearea/MekanismGeneratorsconfig.GENATORS.FISSIONSURFAREAREATARGET.get ());););););););););););););););););););

}

Surfacearea and Fulassemblies are calculated by another function.

@outerride

Public FormationResult Postcheck

MAP Map = New HashMap <> ();

Set Fulassemblycoords = New HashSet <> ();

Int Assemblycount = 0, Surfacearea = 0;

For (Blockpos Coord: Structure.internallocations) {blockity tile = worldDutils.gettilentity (worldd, chunkmap, coord);

Assemblypos pos = New Assemblypos (Coord.getx (), Coord.getz ());

Fulassembly assembly = map.get (pOS);

If (Tile Instanceof TilentityFissionFulassembly) {{

If (assembly == null) {

Map.put (pos, new fulassembly (coord, false));

} Else {

Assembly.fulassemblies.add (coord);

}

Assemblycount ++;

// Compute Surface Area

Surfacearea+= 6;

For (Direction Side: Enumutils.directions) {{

Iflassemblycoords.contains (coord.relative (side)) {Surfacearea- = 2;

}

}

Fulassemblycoords.add (coord);

}

...

}

Elastic

}

Each fuel component is traversed, and each component provides 6 surface area and traverses its six sides. If it is adjacent to the components that have been traveled before, 2 surface areas (it and adjacent components) are deducted. At the same time, each component provides provides each component.1 component count.

So Surfacearea is the surface area of ​​the fuel component, and the number of fuel components in Fulassemblies.

So the return value of GetBoilefficience is the Getboilefficienter in the surface area / fuel component / configuration, and the last one is usually 4, which is another tutorial post:

Boiling efficiency = min (1, fuel component surface area / number of fuel components / 4)

Next, we must discuss in two cases. If the coolant is water, after some calculation:

The heat change = -Clamp (boiling efficiency * (temperature -373.15) * heat capacity / 100, 0, water volume) * 50

CLAMP (x, a, b) indicates min (max (x, a), b), that is, pin X to the middle of [a, b].

If the cooler is sodium steam, after some calculation:

The heat change = -Clamp (boiling efficiency * (temperature -373.15) * thermal capacity / 5, 0, gascoolanttank.getstored ()) * 5

These two /100 and /5 will be made up. For sodium steam, the absolute value of the loss of loss during the calculation process is smaller.

Although you know at a glance, the heat of sodium is about twice the water.

 Private void burnfuel (level world) {{

Elastic

Double storedfuel = fueltank.getstored ()+burnremaining;

Dough topURN = math.min (math.min (Ratelimit, StoredFuel), Fulassemblies*mekanismGENERSCONFIG.GENERATORS.BURNPEMBLY.Get ());););););););););););););););););););););););););););););););););););););););););

Elastic

BurnRemaining = StoredFuel%1;

Heatcapacitor.handleheat (Toburn*MekanismGeneratorsconfig.GERATORS.ENERGYPERFISSIONFUEL.Get (). DoubleValue ());

Elastic

}

This is the logic of controlling heat improvement.The Ratelimit is the speed limit set by the player.MekanismgeneratorSconfig.GENERATORS.BURNPERASSEMBLY.GET () is the efficiency of each fuel component configured, defaults to 1.

Fuel consumption = min (speed limit, storage fuel, number of fuel components * per fuel components per fuel component) => min

The heat change = fuel consumption * configuration per fuel efficiency (default is 1000000) => 1000000 * fuel consumption

Different from the damage value, the higher the temperature, the faster the heat dissipation, so the temperature may remain at a very high point without any visible rise.

Cooling agents, fuel, and rate limit will affect the input consumption, but only the controlling of the player's input and speed limit of the cooling agent (after all, although the fuel consumption is very low when the fuel is small, I think that you can fill in a rate limit is more convenient.To.

Temperature has no effect on the actual operating efficiency. If it is the first time you play this thing, you can choose to lower the brain without lowering.

The core mechanism of the fission reactor is introduced here, and there are some pits related to it.

0x05 盖革计数器在响啊啊啊啊啊啊啊啊啊!IntersectionIntersectionIntersectionIntersectionIntersectionIntersectionIntersectionIntersection

This part is about the radiation mechanism, and then fill in the pit.

0x06 What are this? 32, lose, the reactor is fried

This part is about the adjustment rate limit, and then fill in the pit.