-
Notifications
You must be signed in to change notification settings - Fork 150
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
Implemented throttleable Large turbine #1604
Conversation
This is not just a possibility: IMO this is an anti-pattern
Breaking encapsulation to do policy overrides usually leads to unmaintainable code. Policy should be injected into the place where the state exists otherwise there is no way to reason about whether the object is in a consistent state or what implications any future change may have. This could be done with more focused overridable methods that don't change the internal state in subclasses. I think this throttle functionality would be better implemented in FuelRecipeLogic with optional enabling by subclasses. It could be something as simple as the following methods to override which would also enable other more general uses.
I also find it hard to read your changes when large amounts are unnecessary code reformatting so I haven't looked at the rest of the changes. |
first of all sorry for that I added a shortcut each time I save a file idea reformat it according to the standard present in the GTCE project and I didn't pay attention they was this much of reformat.
You're absolutely right I know breaking encapsulation is often a bad idea I just didn't found better way to do the job at the moment.
This could be a good idea but other multiblock already use the throttle functionality stored in the meta tile entity then it feels a much proper way to implement it in one class each multiblock that want to use it can extends. I we wanted to implement it in the recipe logic it would be needed to be in a lower level to avoid duplicated code.
Moreover the best I've done so far is not the best way you pointed some big problems in my code and think I have found a solution to some of them. The |
IMO = In my opinion https://en.wikipedia.org/wiki/Anti-pattern |
thank you for those precision in deed this looks like a not so good idea but some idea like that was already in the Large turbine code see this and this If anyone want to share some opinion/ advice or things I should take in account about remaking the |
just realize that fuel recipe logic is all ready in the api gonna implement as proposed with some slight modification |
I mostly agree with @warjort regarding this. We should try not break our addons even if they are using common part of mod. Regarding antipattern and best practices we should not add any more technical debt and try to leave edited files in better way then they were before (of course going on cleaning spree is not desired). As you mentioned that you have idea how to rework then please do and I or someone else will do review on new version. |
What have changed :
On the Gregicality side : Those changes break a little of addons but nothing to difficult to fix. |
The canProduceEnergy() and canConsumeFuel() don't appear to be used? |
I think perhaps these new methods should be using doubles instead of floats. It probably won't make much difference but it will keep it consistent and avoid unnecessary casting like that above. |
they are used in the update method |
The ThrottableMultiblockController looks to me like it would be better implemented as a kind of throttle widget rather than a subclass? Similar to how things like filters fit into covers. I know this is not how the LargeBoiler does it but it is something to think about. |
done this is indeed better |
I mean nothing overrides those methods. They always use the default method which just returns true. |
That's just me who plan to do proper things and forget the Large turbine should use it |
currently only multi-block can throttle and I think this will stay. To my mind it's easier to change the class you extend and put a little of code in the workable handler than using a widget. |
The large turbine now use it as always intended |
Yes but it limits its reusablity, e.g.
But since this is implementation detail in gregtech.common somebody can probably change it later as usage requires. |
...va/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeTurbine.java
Outdated
Show resolved
Hide resolved
src/main/java/gregtech/api/capability/impl/FuelRecipeLogic.java
Outdated
Show resolved
Hide resolved
the slicing will get removed as it does nothing on the responsiveness of the throttle this is just some remnant of an older try. I don't think canProduce energy should be by default I will close this current mess and go on a fresh start to make things cleaner much easier to understand and as little invasive as possible. I think this PR will get splitted in 4 :
this way PR should be smaller with much precise and defined goal moreover they should be easier to verify if they are not breaking anything. |
But that isn't how the turbine works. The turbine doesn't care if there is a recipe running when it produces energy. |
well the idea is the to separate the recipe running and the power production by default canProduce energy would be isRecipeRunning and isRecipeRunning would be remaningTime > 0 allowing for more customization on what decide if the recipe is over, which is used on many mechanics and I will probably need to override this behavior while still providing the ability to the turbine to produce energy while there isn't any recipe running. on the current code state of this PR the turbine stop producing power when no recipes are running yet another mistake I've done. |
That plan with separating this topic to multiple PRs seems reasonable to me. It will be easier to define scope, expectancy and some outline. Also it will be less work on reviews. I think we should make sure that we have clear view and understand what we want to achieve before writing code as it will save lots of time. |
Okay so here is what I propose for the first PR :
|
There's kind of an issue here. It is hard to tell what is accessors (exposed for display to the user) I would suggest using getXXX() for accessors and calculateXXX() for policy.
I assume this is just a helper for calculateRecipeDuration(lastRecipe)
I wouldn't introduce unused code. If somebody has a use for it they can add it.
Ok
I don't understand the use for this? In the existing PR it is never overrridden, it is always true.
Ok. You should mention that the actual energy is calculated via the existing getRecipeOutputVoltage()
This already exists, it is called isActive() and has important uses throughout the api
Ok. But I would suggest calling it calculateFuelConsumptionMultiplier see above.
This is the most important part of this PR. You need to define more concretely what this means.
This already exists as a protected field |
👍
if this method already exist or will be added (don't remember ) it will be included.
just in case if someone wants to use it while trying to make this class more flexible I we should do it once for all. I don't know if a futur machine or an addon machine may want to disable his consumtion under specific circumstance or just if he want to do some computation right before the fuel gets consume (even if it may not be the best place )
No, getRecipeOutputVoltage() is just a getter it's startRecipe which compute the recipe output voltage.
I'm willing to use this function as a replacement for this.recipeDurationLeft>0 in all the if statement that use it becose I will need to override this behavior. Is this a correct use for the isActive ? as far as I know when a recipe end the tick after the machine check for another recipe to start but doesn't change the state of isActive.
yeah that's why I propose a getter might be usefull but as you said no unused code so it won't get added. calculateStaticEnergyEfficiency return a scaling factor which will be by default, applied to the output voltage. this operation will take place in the startRecipe method and therefore will get called once every time a recipe start. it define a constant efficiency that could not be changed without reseting the recipe or at all. calculateDynamicEnergyEfficiency return a scaling factor which will be called just before outputing energy to the hatch. hte factor will be applied to the recipeOutputVoltage field which is the cached value returned by startRecipe. this factor will also be applied in the getRecipeOutputVoltage() in order to return the effective power output of the machine. |
Not for the current turbine. You are describing how the default FuelRecipeLogic works. LargeTurbineWorkableHandler's update() method calls getRecipeOutputVoltage() every tick and outputs that amount of energy. To make this backward compatible, you would need to do something like:
Although, if you are certain nothing besides the turbine overrides getRecipeOutputVoltage(), you could just use that method directly instead of introducing a new method. |
Does that mean you are applying 2 scaling factors? First scaling it in startRecipe() and then again in update() |
Don't introduce unused features. Besides making the code more complicated and potentially adding unnecessary constraints for future changes, you have no way of testing if it works as intended. 1st law of Programming: If its not tested it doesn't work. :-) |
It is the other way around. The active status is changed based on the recipeDurationLeft (as well as being under external control). It is this active status that the rotor handler uses for its behaviour as well as things like lighting up the controller as feedback to the user. You don't explain why you need to change it? |
Or I can just say : getRecipeOutputVoltage() is meant to return the output voltage of the recipe so using it directly will be completely harmless except for the people who have changed it to not return the output voltage of the recipe which I hope no one did because it would be very weird .
Yes I apply both of them. the static computation is here for efficiency if you have any computation and your efficiency evolve at most once every recipe change it is recommended to override only the static. If you have an efficiency that need to be updated every tick then the dynamic version should be overrided now if your efficiency is compound of both par then override both methods. the main idea is to try to keep things as light as possible by removing useless /unneeded computation.
as said we should do it once so no one would need to do the job another time we are talking about something in the update method of class that's not an easy task to override this part of the behavior in a clean way.
come on I don't think it is not possible to not test it at first and I'm not sure that testing an if statement with a true inside make sense x) still we could make a button in a turbine to switch it and see how that it runs without any fuels to test it if you absolutely want to test it ;-)
this is why I proposed another function that work the way I want even if as you said something which heavly looks like what I want exist we should find a way to make a clear an understandable difference between those two methods.
yes it was obvious in my mind but as you're not in my mind it is not. In the current try I changed recipeDurationLeft by a float and changed all this.recipeDurationLeft>0 by this.recipeDurationLeft> getConsumptionMultiplier() so as as far as I know I will have to do it again bu only on the large turbine side I would like to be able to do it in a much cleaner way. |
current debate state :new methods which looks ok so far
new methods I forget to put in the list and didn't understand that warjot was pointing this out (guessed as ok) :
new methods still in debate
more detail on some methods :
planned changes to the updates method (not yet approved) :the update part will be splitted in three big part :
Energy Fuel Recipe this is I think the current state of the planning of the first PR. Tell me if you think I have misunderstand something (I do it too many time ) or missed something. |
I was looking at the original feature request and what that says makes more sense to me than what you are trying to do?
Though I don't think that request is entirely correct. What this should be doing for the turbine is to have your single widget for the throttle% If say you have 50% throttle, you consume less fuel, but the recipe doesn't last as long, so you get less ticks on the rotor to accelerate it. I don't think for the turbine you should be doing anything to the output voltage directly. It should just be left as it currently is based on the rotor speed and quality. Having a multiplier for the output voltage still makes sense for more traditional generators like the diesel generator, where the output voltage is directly related to the fuel. |
according to what I remember the throttle doesn't change the boiler max temp the throttle is applied to the fuel and the steam output which in the case of the turbine I transpose the steam output to the power output which makes perfectly sense. after checking the throttle change the recipe duration which makes no sense to my mind. let me explain : when we throttle we expect to produce less but we also expect to consume less with a loss of efficiency which is displayed on the display but with the current implementation the throttle only change the output keep consuming as much as if running full throttle and add a efficiency loss over this. I fact I'm asking myself if the current throttle does change something to the output |
In principle, reducing the throttle should mean you can do more work (total energy), i.e. it is more efficient. Running at full throttle lets you do things quickly but at reduced efficiency and therefore more fuel consumption. Think about driving a car in first gear. You could do the whole journey in first gear which would use less fuel, but it would take forever because you can't accelerate to any great speed. In practice, things are usually more complicated than this simple (spherical horse in a vacuum) explanation. :-) |
But I am arguing it doesn't make sense. The correct transposition is steam output -> rotor accelarations. |
with the current implementation everything gets decrease by the throttle multiplier the total output per recipe duration is lowered (if not the current implementation does not work) but the fuel used per recipe duration isn't lowered as both get multiplied by the throttle multiplier leading to the conclusion that the total output per fuel used is lowered meaning the current implementation heavily decrease the efficiency of the boiler. the rotor acceleration is more like the boiler heat has you consume less fluid you produce less power think in term of real life when we want to throttle our turbine we do not reduce there speed we reduce how much steam we send to the turbine and lower how much power is consumed (it's the other way around the power consume first decrease then the steam send but that's the same behavior in the end) by reducing how much steam we produce per tick we reduce how much power we generate per tick not how heat is the boiler. |
I can't say I am big expert on the algorithm in the LargeBoiler. I have never used this feature. What is relevant is that a multiplier exists which affects the duration of the recipe. So changing the duration, changes the number of isActive() checks that returns true, which then changes the number of temperature or rotor speed increments. How the consumption multiplier and other multipliers change with respect to the duration multiplier is just a tuning issue. |
So my previous comment was misleading. It is temperature -> rotor speed that is the correct transposition. |
the current implementation in the large turbine doesn't change the duration of the recipe I don't really see a way of changing the recipe duration while keeping the power/fuel ratio stable before applying throttle efficiency. still you make a point and this may be intresting to take this in account |
That is the feature you are adding. :-)
This should all come out in the wash when you change the consumption based on the throttle. |
The other issue is the third component of the throttle. For the large boiler this applies a steam output multiplier. So the question is should this do something similar in the turbine, i.e. apply voltage output multiplier as you originally implemented it, or should it be as the feature request suggests, a multiplier applied to the max rotor speed. From a physical point of view, what the throttle is really doing is changing the flow of fuel through the rotor. |
well humm yes but actually no I'm dinamicaly changing the how fast is consumed the duration to balance throttle change by artifficially increasing the effecive consumption
I define efficiency as power/fuel so it would be consumption and and production but yeah this something we should discuss when we would be planning the 4th PR |
yes but not mendatory if you produce less power you produce less resistance to the turbine moovement so you can still run as fast as previously. Once again think about turbine in real wolrd we keep them at a stable speed no matter how much they produce. We do this because the stability of the AC frequency is important but I here mean that it is possible and still logical to keep the same speed while reducing both power output and fluid input. |
I will start stating that I am not expert on turbines or driving (either car or spherical horse in a vacuum). And I think that most of our players are not too (at least first part) so mechanic we come up has to be ease to understand for everyone. Without need to explain it through many paragraphs of text full of mathematical formulas. If we can't we are not touching this! How I think turbines should work: Now this is probably not accurate to real world. But it does not have to be 100% (of course it should not be total bullshit) this is not simulator it is game mechanic to make it fun. Also just because I said how I think it should be does not mean anyone will go implement it that way. This is open for discussion and I believe that other GTCE Council members will have something to say. Also for all discussion compatibility should be in mind. |
What:
Implemented feature proposed in #1548 : make the player able to throttle Large turbines like large boilers.
How solved:
I extracted code from the Large boiler to an abstract class
ThrottleableMultiblockController
and then makeFueledMultiblockController
extend it. For compatibility sake two constructors has been made one with thecanThrottle
argument and one without considering the argument asfalse
by default. This way any code based on theFueledMultiblockController
should work the same without any issue.Created a
ThrottleableRecipeLogic
class to handle machine throttle and madeLargeTurbineWorkableHandler
extending it and changed some methods fromprivate
to protected in theFuelRecipeLogic
as I was needing to override some part of it.I added some information about fuel consumption rate making setting the correct throttle easy.
I also added a new shortcut on setting throttle : pressing control multiply the change by 10 leading with :
Outcome:
Implement : #1548
Additional info:
New large turbine's controller GUI :
The boiler's controller GUI didn't change :
Possible compatibility issue:
Any machines that extends
MetaTileEntityLargeTurbine
will have to either support throttle or addthis.canThrottle = false
after calling thesuper
constructor to disable the throttle ability.