#1.20.2
1 messages · Page 4 of 1
neoify is pushed
it doesn't set permissions? 
Wrong org, was looking at the mcf one
I am about to hit the shower I will setup TC then afterwrads
Okey created a patch for Srg2Source
Had to rip out ProGuard
But got an updated compile target
Possible this question should wait until 1.20.2 is out, but what's the plan for ChunkWatchEvent.Watch with the chunk networking changes?
depends on how long this moment is
Ah, thanks! Had searched for the class name, not the one without spaces :)
It is not actually the networking
We fixed all the networking problems, but in reality we have more of a problem with the complete reimplementation of ChunkMap which is entirely re-written, and now uses section pos.
What's the difference between sections and chunks again? Are sections the 16^3 region?
Yep
It is a relatively easy fix
But we still have not found any nice place to wire up the events
Yeah. I mostly ask because the Watch event is used for sending additional data alongside the chunk, and for tracking watching changes, so somewhat wondering if it makes sense to split the event instead.
Personally I don't know.....
The chunkmap is currentls still above my capabilities and time availability to understand
Oh yeah, definitely not the main thing to focus on right now, mostly being nosey. Thank you!
did you implement the spec thingy from modmuss?
No...... Not at the moment......
Nothing was done for configuration afaik
orion did you manage to genPatches yet? I added the access changes as AT entries to the forge AT (they still need to be placed in the right place because I just put them at the end of the file)
Not yet
was networking fixed
Yes
At least it is functional
But the proposed protocol change was not implemented
but you will right?
When I get the time
i just got the mental image of that one Star Wars meme with Padme asking Anakin 
[Jump to referenced message.](#minecraft-updates message)
ugh ... #minecraft-updates message
can someone make camelot say what channel the message is from? :P
<> it 😛
too late it already deleted my message :P
but the ugh is more that it doesn't embed the embeds so it doesn't show the context :P
well if it can't then I would hope for like, a hint to what the target is :p
anyhow pre4 is out
have fun porters
who's got a psychic api /s
do the bell and all
<#neoforge message>
Update to java 17, and latest JDT
@mental carbon did you make sure that s2s is run with j17 using toolchains?
Yes
orion I don't think these are correct
checkAt's is yet to be run
I did and I think it checks with the wrong mappings because it flags the correct ones as invalid
Interesting
it also says it is missing group entries but when I check which field that is with the bot it says it doesn't exist (using 1.20.2-pre1 for the bot mappings)
iirc the groups are re-generated, when running checkAts
does the new field or an old field not exist?
?
Those were the names used in the patches
why is there a missmatch between patches and bot then
!moj BuiltInPackSource.fixedResources 1.20.2-pre1
Unexpected error occurred dispatching command. Please report this to your bot admin.
That is beyond my understanding
But I know that those worked without issue on my end
And the ones that the bot produced did not
So I went with the ones in the patches
u sure?
I knows about the snapshot
But i have no idea if it knows the srg names or ids for it
Actually NeoForm ids
so it is a missmatch between mcp srg and neoform srg?
it doesn't know about srg snapshots* because there were none public before
plus it looks it up from the forge maven, not the neo one. I'll try to update it but also neoform doesn't have non-timestamped versions so it's going to be a bit more painful and seems like useless work if we're soon™️ getting rid of srg
yeah
that's the k9 bot right?
the one we're getting rid of?
as well as the removal of srg
this seems like multiple layers of pointless
yeah. but mappings are not currently a goal for camelot when https://linkie.shedaniel.me/ is a thing
View mod dependencies; Search up mappings; All on your web browser instead!
fair
Okay, all of client package patch auditedcleaned.
question: why are there so many mapping targets
I'm doing checkAT's
there's no "vanilla". you can use srg/intermediary/hashed if you want to convert to/from mojmaps
@thorn robin take notes
also, for worse or for better, linkie web allows you to view vanilla sources lol
because there's a lot of loaders 
and each have their own intermediary
this doesn't do snapshots for srg either
because srg doesn't have public snapshot versions
neoform
I think we are done with pre1
4 ate 2 3
I believe we skip 2 and 3
orion don't forget to push
Yeah I am not done cleaning stuff
@dull bison This is not a good solution:
public static final Codec<Ingredient> CODEC = net.minecraftforge.common.util.ForgeExtraCodecs.withAlternative(
ExtraCodecs.lazyInitializedCodec(() -> net.minecraftforge.common.crafting.CompoundIngredient.DIRECT_CODEC.flatComapMap(java.util.function.Function.identity(), i -> i instanceof net.minecraftforge.common.crafting.CompoundIngredient c ? DataResult.success(c) : DataResult.error(() -> "Not a compound ingredient"))),
net.minecraftforge.common.util.ForgeExtraCodecs.withAlternative(
REGISTRY_CODEC,
VANILLA_CODEC
)
);
We can not special case the compound like that
We need a way to deal with this
Other then just special casing it I am sorry
That's just for the direct compound ingredient based on a list, right? The third one of these: #1136320550168436798 message
Yep but the simple answer is: That is not a valid option
Only alternative is to just not allow that syntax
Period
Given that the compound type is not the vanilla type, their is no way you can leave out the type in the first place
As such I see no option other then to disallow it
I mean, the current special casing of it works, but I guess the worry is that it's confusing as it's not a vanilla thing?
Correct, and we do not allow any other none vanilla ingredient, or other subsystem for that matter, to do this
To override the "default" tag just because it is not an array or an object
I mean yeah but, and I can't believe I'm the one saying this, doesn't neoforge normally special case itself relative to other mods all over the place? Like, I'm all for not giving neoforge special treatment in this regard but I feel like you'd have to start doing that everywhere to be consistent
But yeah, solution is to just not allow that syntax
not quite we had that special case before changeing to codecs because it is a valid syntax for vanilla ingredients
if it does then it should stop doing that where it is (where feasible)
the type CompoundIngredient is forge but the list syntax is vanilla but without that special casing can only do item and tag values
I would agree but most people don't. It's the whole "we can change vanilla expectations" thing.
Oh. Yeah I'd say special case it then
As otherwise that's even more confusing
Actually the list syntax for vanilla is only items
Wait wut? Mojank I guess
1.20.3: "ingredients now use holdersets"
return fromValues(StreamSupport.stream(jsonarray.spliterator(), false).map((p_289756_) -> {
return valueFromJson(GsonHelper.convertToJsonObject(p_289756_, "item"));
}));
Given that vanilla has the format claimed
but yeah I concur that all non-vanilla ingredient formats should be a json object with an explicit type field
oh nevermind that's just a horrible method name
That is a big no vote from me on the cusotm compound handling
well in vanilla 1.20.2 it supports tags
if you need compound based handling then specify it
it treats the array entries as Objects, the usage of "item" there is only for error logging despite it being totally irrelevant
So, to clarify: what does the list format allow with the special casing that it doesn't in vanilla?
we had exactly this handling in previous versions tho
if list -> compound
I believe our extensions allow the list to be any ingredient
Otherwise vanilla only calls valueFromJson for the array elements
which cannot produce a full Ingredient
Because best I can tell this special casing makes perfect sense? You're basically abstracting the vanilla ingredient behavior, and when deciding how the vanilla list fits into that you say it's a compound, which fits into what vanilla does
there is no more valueFromJson its all codecs now
But to clarify further: what's the actual codec used for members of that list in vanilla?
well.. does the vanilla list ingredient use a list codec of the ingredient codec?
the list members in vanilla are Ingredient.Value
Hmm. Then I wouldn't special case it
is that a yes or a no, I forget how ingredients work 
and the special casing changes that to be Ingredieng
no
aw
If the vanilla intended behavior was "union of ingredients" they'd use the root ingredient codec directly. This would allow, like, nesting of lists which seems weird
private static Codec<Ingredient> codec(boolean p_301074_) {
Codec<Ingredient.Value[]> codec = Codec.list(Ingredient.Value.CODEC)
.comapFlatMap(
p_300810_ -> !p_301074_ && p_300810_.size() < 1
? DataResult.error(() -> "Item array cannot be empty, at least one item must be defined")
: DataResult.success(p_300810_.toArray(new Ingredient.Value[0])),
List::of
);
return ExtraCodecs.either(codec, Ingredient.Value.CODEC)
.flatComapMap(
p_300805_ -> p_300805_.map(Ingredient::new, p_300806_ -> new Ingredient(new Ingredient.Value[]{p_300806_})),
p_300808_ -> {
if (p_300808_.values.length == 1) {
return DataResult.success(Either.right(p_300808_.values[0]));
} else {
return p_300808_.values.length == 0 && !p_301074_
? DataResult.error(() -> "Item array cannot be empty, at least one item must be defined")
: DataResult.success(Either.left(p_300808_.values));
}
}
);
}
it does this™️
that is why the special casing is needed
Vanilla doesn't have Ingredient subclasses, only Value
so vanilla wouldn't use a list of the root, because there is only a single ingredient object, which is one or more Values
Hmm. Yeah, that's a weird case then
But since we have custom Ingredients, we should expand the array syntax to support full ingredient objects in the array
Here is what I would say: in vanilla, the list and the single item codec that are combined with an either are the same
We should have the same behavior
So, if vanilla is "value or list of values", then we should have "possibly custom value or list of possibly custom values"
Lets just make the CI need a type
Basically, I'm saying keep the special casing
That takes all guessing out of the room
custom values are not feasable for a forge system because the checking is done on ingredient level
For every other system we have that you need to supply a type
To get the custom logic
Because whatever I'm allowed to put into the root thing, I should be allowed to put into the list
Yeah, that's what I mean. That's why you have the compound ingredient
So, your special casing of this is correct
Basically, if I can do```json
"ingredient": {
"type": "whatever",
...
}
Then I should be able to put that same structure inside of a list and have it Just Work™️. And I believe that requires this special casing to use compound ingredients
That is not how any other system works that requires custom forge logic
Because any other forge system uses the vanilla logic for the "no type" specified scenario
It generally is though. Granted, generally it is that way as a side effect of the list thing being a list of the root codec that forge changes
But frankly, anything else is confusing
No it is not
Even if it is a list
Yeah, it will be very awkward if
"input": [
{
"item": "minecraft:stick",
},
{
"tag": "#minecraft:cobblestone"
}
]
is legal but
"input": [
{
"type": "forge:partial_nbt",
"item": "minecraft:stick",
"nbt": "{\"blah\": 10}"
},
{
"tag": "#minecraft:cobblestone"
}
]
is not
Why?
since it is legal now
Because that's fucking confusing, that's why
Yeah, and I am arguing that it should not be legal now
this is what we did in 1.20.1
public static Ingredient getIngredient(JsonElement json, boolean allowEmpty)
{
if (json == null || json.isJsonNull())
throw new JsonSyntaxException("Json cannot be null");
if (json.isJsonArray())
{
List<Ingredient> ingredients = Lists.newArrayList();
List<Ingredient> vanilla = Lists.newArrayList();
json.getAsJsonArray().forEach((ele) ->
{
Ingredient ing = CraftingHelper.getIngredient(ele, allowEmpty);
if (ing.getClass() == Ingredient.class) //Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later
vanilla.add(ing);
else
ingredients.add(ing);
});
if (!vanilla.isEmpty())
ingredients.add(Ingredient.merge(vanilla));
if (ingredients.size() == 0)
{
if (allowEmpty)
{
return Ingredient.EMPTY;
}
throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined");
}
if (ingredients.size() == 1)
return ingredients.get(0);
return new CompoundIngredient(ingredients);
}
if (!json.isJsonObject())
throw new JsonSyntaxException("Expcted ingredient to be a object or array of objects");
JsonObject obj = (JsonObject)json;
String type = GsonHelper.getAsString(obj, "type", "minecraft:item");
if (type.isEmpty())
throw new JsonSyntaxException("Ingredient type can not be an empty string");
IIngredientSerializer<?> serializer = ingredients.get(new ResourceLocation(type));
if (serializer == null)
throw new JsonSyntaxException("Unknown ingredient type: " + type);
return serializer.parse(obj);
}
Since I find that confusing as all hell
Because now all of a sudden
Were that original syntax was previously a normal ItemPredicate
It is now something completely different in code
The expansion for Ingredients is to be able to deserialize Ingredient instead of Value - that should be consistent across all places that deserialize Value, including this array syntax.
How so? In vanilla, it allows "single ingredient or list of ingredients". You seem to want to allow "single ingredient including custom types or list of ingredients but only vanilla types of ingredients
No it does not
That is were your understanding is wrong
It allows a single ingredient value, or a list of ingredient values!
It does not allow a single ingredient, or a list of ingredients!
It never had
in vanilla those are the same thing, orion
We only made it that
No they are not shadows
Especially now
Where they explicitly have custom intermediary logic to deal with tags and other options
Sure. But we're switching out the root ingredient value codec for a root ingredient codec based on dispatch, eithered with the value codec
No we are not
We are switching out the codec for the ENTIRE ingredient
Not the values
The value parsing is identical if it detects a vanilla ingredient (so missing type, or it being an array)
Okay, I'm just saying that allowing this:```json
"ingredient": <whatever>
But only allowing this for certain whatever but not others:```json
"ingredient": [ <whatever> ]
Is just confusing, full stop
Why?
Because that's confusing? What makes the one special that it only works on the root context?
because it worked before
The array always represents a vanilla ingredient with multiple values
that would be stupid ^
Just because something worked does not mean it was right
Why? What is the problem of specifying that you need a compound ingredient with an array of inner ingredients
Because the array syntax exists
Note that the types that are parsed in the two cases are significantly different
and since it exists, it must be expanded in accordance with the non-array syntax
Okay. Let me explain. From a user perspective, who hasn't seen the ingredient class and just sees the vanilla ingredient types and forge's added ones, the difference between a "forge added ingredient type" and a "vanilla ingredient value type" at the root level is... Non-existent, basically. From what they can see there's a vanilla way of specifying an ingredient, and a forge extension. Now, they're used to being able to take that vanilla way of specifying an ingredient, best it in a list, and have it Just Work. If this suddenly doesn't work with forge's special things (which look equivalent from what they can see! Remember, they're both JSON Objects here, it's not like ones just a string or something), that is very confusing
Because if you don't do that, you are changing the definition of an ingredient from "one or more Values" to "Maybe one Ingredient, or more than one Value", when it should be changed to "one or more Ingredients"
But there is: The specification of the type field
It has always been there
In any other subsystem
If you need something other then vanilla behaviour
You need to specify the type
The type field expands Value to Ingredient, it must do this unequivocally, not partially
That has always been the common context of every custom forge data driven sytem
failing to do this in the list syntax becomes a partial implementation of our own spec
No it does not, it did not in the past and it did not now
It never has shadows
It always specified the type of ingredient
Yes. But my point is that in other such systems, the thing represented by "thing without a type field" and "thing with a type field" aren't fundamentally different types of stuff
Never its values
it expands the parser from parsing Value to parsing Ingredient
Correct which even makes it more important that in this specific case were the type internally even changes we are explicit with what we expect the user to enter
No it does not, it never did, and it does not now
it does this at the base level and at the array level
but whatever, you're being stubborn and I'm tired of arguing, You have three people telling you not to change it, so do not change it unless you pull a maintainer vote and get approvals.
It made the parser of the compound ingredient mimick vanilla behaviour
But it never expanded the value parsing it self!
No, that's the opposite. Like, at the root level the two are interchangable (from what people can see in JSON). They should be interchangable at other levels too
I would like to point out that, the way we expand holdersets, you can have a list of ids (a vanilla format), but you can't have a list of holdersets without using an explicitly-typed holderset
this sounds a lot like the ingredient problem
Exactly!
I'm trying to look at this from the "writing a datapack" perspective, ignoring what the code looks like
I'd argue that should be changed too then
And registry logic this all holds
No it was even explicitly decided not to
but that's haaaaard
Decided when? By who? Why can't we change that?
I might have decided that 
They still are, by using a forge typed thing in the list?
We can, but other then, well it worked in the past, I have not seen a reason why we should change that
No they are not
If we switch to the behaviour as request
Then any array will ALWAYS be a compound ingredient
Regardles off its contents
Okay, alternative idea here that solves this. Parse in a series of eithers, in this order:
- single value
- list of values
- single ingredient
- list of ingredients
And that is an implicit change that is not choosen by the user
That way arrays of just vanilla stuff are still parsed the vanilla way
or we could just not expand the vanilla list format
The question in the room is, should we?
it sounds like more work
IMHO: We should not, because it confuses work
See #1136320550168436798 message - that is very confusing if you haven't seen the class underneath
it would've been more work to expand the vanilla list holderset
It is a secondary codec, it is trivial to do
I just made the codec so it works with the recipes in the testmods
But my point is, I very much want consistency across forge features
And this is the thing that stands out in this case
If it's changed on the ingredient stuff it should be changed for holder sets too, assuming they're really equivalent, I agree
If you can come up with a functional argument why the array should be implicitly changed, without the consent of the user/datapack maker, then I am all for it
But not without
As I've said that's not necessary...#1136320550168436798 message
the main reason the vanilla list holderset wasn't expanded was because I just made the eithering go like
"if it's an object, read it using the forge stuff, otherwise use the vanilla parser"
consistency is good but it can only be across systems that are similar in vanilla which ingredients are not (thanks mojank)
It is not really because there is no way to know if a codec provided by any modder, also would like to parse an array
and also like... vanilla's list holdersets is not a list of holdersets
it doesn't even read tag ids, it can only be a list of single ids
this is not holdersets because ingredients don't even work with holdersets at all
Not yet! But they easily could
right, I'm just trying to point out something analogous
If we send the ops from the recipe through
last time I looked I came to the conclusion that it would not be easy to make ingredients holdersets 
I'd argue they should already but the fact is they don't
It is now.........
oh, neat
We are basically one step away from passing in the entire registry ops with support for holder sets into the ingredient parser
well... no, because ingredients can be nbt-sensitive
But we did not
err I guess you could have holdersets as a type of ingredient
Because well they might be unforseen concequences
we are already doing that
No it is just pure JsonElement stuff still
There is no ops and element data currently available
that's just the helper
That is what is invoked from Recipe
I'm just looking at this from the perspective of "i haven't seen the class, why isn't my stuff working" and this seems extremely confusing if it doesn't allow custom stuff in lists. Like, codecs and JSON structure should not reflect code exclusively; they should make sense to someone who hasn't seen the code! There shouldn't be "gotchas" you have to watch out for introduced by forge changes, which is what this seems to do
Actually nevermind
nope
So you consider:
"ingredients": [
{
"type": "something",
"thing": "this"
},
{
"item": "minecraft:block"
}
]
to be just as readable and understandable as:
"ingredients": {
"type": "forge:compound_ingredient",
"values": [
{
"type": "something",
"thing": "this"
},
{
"item": "minecraft:block"
}
]
}
But if the worry is "don't want to parse vanilla stuff through forge path"... Just put the vanilla codec first in the either and it'll all go through that route first and parse any lists with only normal old ingredient values in it
oh wow that does make it annoying
Yep, that's the issue here
Vanilla ingredient values are objects. Forge's fancy stuff are objects
then yeah I think forge should allow forge ingredients in the vanilla list of objects
(if feasible)
You want to know something really silly?```json
{
"type": "custom:whatever",
"item": "minecraft:iron_ingot"
}
...this'll parse with the vanilla codec if the vanilla codec gets to it
but as an iron ingot, not a whatever 
Yep
okay yeah we should do something about that
So this is doable but you have to do some extra thinking about how to not have Issues™️
not on forge
Using the vanilla ingredient value codec I mean
the registry codec comes before the vanilla one
Which means that that same thing, used as an ingredient in forge and vanilla, would give different results
it only would if there is no custom:whatever in the registry
In other words, custom ingredient types can break vanilla compat rather easily if anyone happens to register one using item or tag as a field
I honestly dislike this approach to custom ingredients in general for this reason
But it's not like there's many other good options
I mean
Imagine someone makes a datapack with that structure in it. That datapack will have different effects based on whether it's used with vanilla or forge
If this is really such a sticking point
We could leave it schurli
But IMHO we should not expand it
This is really not a good solution
or have a steering council vote 
And this format should never have been accepted and as such adapted in the first place
How feasible is it to have custom ingredient value types instead of custom ingredient types?
Not very, it is also not very usefull
Due to the way the vanilla ingredient works
Oh
there's mojank, right
we can remove that special casing in 1.21
Personally I'd say "yeet custom ingredients entirely and rework it from the ground up"
Because personally we should follow vanilla, and delegate based on a field name (item, tag) not based on a type field value, to avoid possible overlaps
...and to avoid this #1136320550168436798 message
may I present the Ingredient.Value interface
public interface Value {
Codec<Ingredient.Value> CODEC = ExtraCodecs.xor(Ingredient.ItemValue.CODEC, Ingredient.TagValue.CODEC)
.xmap(p_300956_ -> p_300956_.map(p_300932_ -> p_300932_, p_301313_ -> p_301313_), p_301304_ -> {
if (p_301304_ instanceof Ingredient.TagValue ingredient$tagvalue) {
return Either.right(ingredient$tagvalue);
} else if (p_301304_ instanceof Ingredient.ItemValue ingredient$itemvalue) {
return Either.left(ingredient$itemvalue);
} else {
throw new UnsupportedOperationException("This is neither an item value nor a tag value.");
}
});
Collection<ItemStack> getItems();
}
hmmmmmmmmmm
Ah, lovely. I would say allow custom ingredient types but make the delegation happen based on a field name not the type field value
you could patch a codec getter onto Value that defaults to the vanilla CODEC
So it would be:```json
{
"forge:whatever": { ... }
}
no, better idea, make it extend IForgeIngredientValue, which has the abstract codec getter
And they'd all have just one field, like vanilla
can you see the matching logic there
actually, bad idea, nvm
The bigger issue is the list of items... Gotta love that
it's just a getter for a collection of ItemStack
But I believe delegation based on a field name shouldn't be too hard and that would solve the overlap issue
mhh ["#planks", ["#planks", "oak_planks"]] gotta love lists
this is how the tag value works
@Override
public Collection<ItemStack> getItems() {
List<ItemStack> list = Lists.newArrayList();
for(Holder<Item> holder : BuiltInRegistries.ITEM.getTagOrEmpty(this.tag)) {
list.add(new ItemStack(holder));
}
if (list.size() == 0) {
list.add(new ItemStack(net.minecraft.world.level.block.Blocks.BARRIER).setHoverName(net.minecraft.network.chat.Component.literal("Empty Tag: " + this.tag.location())));
}
return list;
}
Oh god this just gets worse with more looking at it doesn't it
this is the test method
public boolean test(@Nullable ItemStack p_43914_) {
if (p_43914_ == null) {
return false;
} else if (this.isEmpty()) {
return p_43914_.isEmpty();
} else {
for(ItemStack itemstack : this.getItems()) {
if (areStacksEqual(itemstack, p_43914_)) {
return true;
}
}
return false;
}
}
Yes
Which is why I am still for the explicit route
Because the vanilla system is JANK AF
And for me personally I would never touch it with a 10 foot barpole
Additionally
If a codec is used to write it
(Which it is by default)
It will never output the array version of the compound ingredient
It will always output the object variant
Explicit route being not allowing in lists? I'm talking beyond just that... Even your registry delegate system has issue whether or not you allow stuff in lists
I'd say have two different codecs
Have the normal one used for delegated stuff be not the array version
Oh wait that wouldn't work nevermind...
Gah, that's annoying
No
No they are allowed in lists, just as long as they are custom lists were it is explicitly allowed
so writing will always prefer the array version
The vanilla list does not allow other infredients
Yet if you specify that you want a compound ingredient that means that you values array now supports ingredients on top of items and values
In other words
If the datapack maker wants to combine different ingredients together he just simply needs to day so
Yeah I'm saying my issues with the ingredient system go beyond that
And if he does not say so we assume vanilla logic, meaning no ingredients allowed
It is inherent to it being vanilla that it is not perfect
So I would say put those issues back in the bottle for now
Will the average datapack maker understand the difference between "ingredient" and "ingredient value"? No. From the perspective of the JSON they're equivalent in vanilla
That depends...... are you assuming a vanilla datapack maker? Or one for modded environments. For the first I would tend to agree, for the later not so much
For the first, and the second should reflect the first in that regard
Because this should be designed from a JSON first perspective
Or rather, in vanilla, "ingredient" is "object or list" and "ingredient value" is "list", so ingredient values look like a subset of ingredients defined by being objects. In other words - if I've got in ingredient in JSON, and it's an object, I should be able to treat it like a vanilla ingredient value because that's the defining characteristic of a ingredient value from the JSON perspective
But if it is for the first, how would the vanilla maker even know that ingredients are valid there
For all he knows is that for a custom ingredient type, he needs the object format with the type:
Because that is what is documented everywhere
Its the fact that it's a JSON object
This is really the core of what I'm saying: #1136320550168436798 message
But you know what
I don't really care enough for this hill to die on it to be honest
I just brought it up
Because it is not standard procedure really
Basically, in vanilla, the way I tell if something can be put in an ingredient array is "it's a JSON object and a valid ingredient". That should hold with forge too
So, if something is a JSON object, and a valid ingredient, it should be able to be placed in the list style ingredients. Forge's custom ingredient types satisfy those conditions
And thus should be able to be placed in the list style ingredients without further changes, from the JSON perspective
Yeah, ingredients are in general
Okey I patched out ItemStackFilter
why that? we created that interface to support custom item predicates
Yeah
It created 150+ hunks
And I made that same logic with 3
I just did not come up with the idea myself sadly
Shrimp and covers dis
CodecCapableItemPredicate that's a mouthful
Yep
But it works
I still don't know if I need an EXC if I change a field like that
I don't think so
Its syntax is the same
Its name is the same
Just its init code changed
Which does not matter I think
may I suggest SerializableItemPredicate
right now I feel uncomfortable with serializable as the prefix for thigns that have a codec
I know it is their purpose
But previously that ment a custom serializer
And people can interpret the wrong stuff into it

Especially when not coming from 1.18 and above
But I like the name none the less
It is shorter
So meh?
Lets just use it?
What do you say?
I am for sure not going to stub my toe on it
Serializable is the more correct word but yeah, aware works too as it's lighter than capable
Coolio
They're things aware of their own codec
orion you do know that ExtraCodecs.withAlternative will never use the second one for writing. Even if the first fails on writing it just errors instead of trying the second. That's why I created the ForgeExtraCodecs version
No that is news to me
Okey I likely need to fix that then
Where did I use the vanilla codec?
it uses the either codec under the hood and for writing just does Either.left
ItemPredicate
it will always use the CodecCapableItemPredicate.CODEC.xmap for writing and never the vanilla fallback
1.20.2-pre4 24 reject files
- RecipeBookComponent
- WorldOpenFlows
- ChatScreen
- Screen
- ClientHandshakePacketListenerImpl
- ClientLevel
- ClientPacketListener
- ServerData
- TextureAtlas
- Minecraft
- BoatDispenseItemBehavior
- DispenseItemBehavior
- Main
- CompoundTag
- NbtAccounter
- NbtIo
- StringTag
- ServerPlayer
- Hoglin
- ThrownEnderpearl
- MerchantOffer
- MinecartItem
- DropperBlock
- LevelStorageSource
Switching to patches in mojmap? 😍
i'd be :D for that
Yep
And at runtime
Basically everywhere
Removes some mapping options
Yarn is now not possible anymore
But parchment still is
JsonThings scripting here we go (please read with mario voice not chris pratt voice)
That means we can dump runtime remapping in GML, which will be nice
Oh no
that's really bad news for ports of fabric mods
although I suppose the ability to run in dev with another mappings set can be implemented externally
Not really? Yarn was only half possible anyways with the fact that Class Names where fixed as MojMaps
^
archloom had logic to correct that
Archloom is based on loom
There's no reason archloom must also remove mappings channels like we did
It shouldn't be majorly effected by what we do? Allthough the change to runtime mojmaps will probably require an update
If people already use it, they can continue to use it. right?
I guess it just means mojmap should be used as the intermediary instead of SRG
Yep
Ok yeah this isn't as bad as it sounded initially
But it gets us patches in mojmap
Runtime mojmap
Etc
No more reobf
The jar you build is simply ran
Removes complexity, removes confusion between potential NeoSrg and ForgeSrg, etc
There's, at least what I think most of the team would consider, little downsides for major positives
RC1 @snapshotalarm
wee woo wee woo
Shit
good luck porting again
...welp. Time to remember what else I wanted to try to write in time for the 1.20.2 BC window I guess
Good luck to the porting folks; I don't envy trying to figure that out
For consistency
<@&1067092163520909374>
https://www.minecraft.net/en-us/article/minecraft-1-20-2-release-candidate-1
https://mdcfe.dev/mc-changes?ver=1.20.2-rc1
(Although think I missed pinging for Pre4)
Yeah it takes a good moment
They seem to have two different processes for releasing the piston-meta file and the blog post
So they can be out of sync by up to an hour
I wanted to do that, but I ain't allowed to ping @snapshotalarm
rc1 :o
RC heck
Also can we start handing out that role yet for those who want it?
I think so
And mdcfe is up to date
wait, does that mean you can run built jars from the mods folder in dev
Yes
I can see it, so must be your side?
Yes
I mean my messages are taking forever to send.
Ah
In forgedev no less, too. That'll save so much pain
Imagine being able to debug modpacks
Imagine being able to reproduce forge bugs in a dev environment
I may need to rerun the update for neoform
I am checking if my patch to jammer now works 😄
ping me when I can start porting again
It will take me a while 😄
@dull bison Actually, if you could do the following:
- Create a new branch named 1.20.2-pre3 from the ucrrent working state.
- Then start the update, using #1136320550168436798 message as a guide, note, you need to delete the projects folders first. Additionally the version of neoform that you need to use is: 1.20.2-pre4-20230913.163358
Due note, perform the update again on the normal working branch for 1.20.2
I just want to keep a nice lookup backup behind in the private repo
If this is ever an optoin an no longer, like, gets attached to an internal channel or whatever the issue was before I would love the role!
the downside is my small remaining concern about the license of mojmaps themselves. but that's pretty much trivia, and smolfry compared to everything else.
i admit that when asked, i recommended when this first came up, to REMOVE the intermediate mapping
mojmaps at runtime was always my preferred endstate.
Oh for sure, I also still feel like people should be aware of that license (existance/contents) when modding
in general, mods themselves will be in compliance with the license. the only ones "at risk" are people such as ourselves, who are doing the dirty with the files. but are we manipulating files (a potential copyright violation) or the data from the file (never a copyright violation). It's such a stupid fine line.
is the tsrg a derivative work of mojmap, or not?
if you consider mojmap a dataset, then there is no inherent copyright. data can't be copyrighted.
tsrg is a file format smh
why pre3 if the version I'm using is pre4?
Sorry anme the branch pre1
Cause that was the pre1 port we just finifhed
Should be pretty fast then?
not really they changed a lot in NBT loading
it failed on one hunk but CompoundTag and NbtIo changed a lot
I'll fix some other compile errors first
we also need to decide on versioning before releasing 1.20.2
For sure
But i need to make massive modiciations to the buildscript
Mostly pressing the DEL key
But still
YEET the remapping!
we have 2-4 days for that
(I think we're not gonna make a same day release this time)
my life will be complete when genPatches has no dependencies, oh the development productivity!
Have you pushed anything so that I can start on the buildscript?
am committing rn
Noice
@dull bison for the future, you push as soon as you update the MC source, and you fix stuff in a later commit
so there's a diff of the changes / fixes
*kinda
Also means we can yeet ObfRefHelper and the remapping code in ASMAPI
and mixin won't need refmaps anymore
will there be a tool for updating ATs? [though hopefully most people will have the moj name commented, which would allow it to be updated with a simple regex]
I don't see why not. Someone should port out the AT magic thats in the Forge buildSrc plugin, into FG for users to use. Validating and such
Okey range maps are gone
I tried in the past; I think I fell into the problem of how to get an MC artifact that doesn't have the ATs applied, to check the ATs against, since otherwise you can't do a check for when an AT is making public something already public, or something like that
been a while since I did that, so I may be wrong 
ah, here's my branch for that
https://github.com/MinecraftForge/ForgeGradle/compare/FG_6.0...sciwhiz12:ForgeGradle:check_ats_task
ugh, ofc its not easy
because FG
there should be some black magic somewhere you can use to grab the renamed but untransformed artifact
as its in the user cache, the file does exist
Personally AT groups are just stupid
so uh on the subject of runtime mojmap and ATs
right now we don't have a problem if one mod makes a field public and another mod makes a subclass with a field with the same name, because the field isn't really called that
what are we gonna do about that? or is it still not a problem because bytecode says the declaring class?
ye
[it is occasionally a problem in dev iirc]
You can have multiple fields and methods with the same name in bytecode but not in source
iirc you can actually (in bytecode) have different fields with the same name on the same class, as long as the types are different
here's what the JVMS (18) has to say on that, per section 4.5,
No two fields in one
classfile may have the same name and descriptor (§4.3.2).
fields.
substantially similar paragraph in section 4.6
No two methods in one
classfile may have the same name and descriptor (§4.3.3).
Interesting
You can hide fields, yes
Yeah those check out as bytecode rules
Won't be a problem, because the bytecode specifies which is being referenced, and because the subclass is basically shadowing the field so it's all fine in that regard anyways
Bytecode allows this while source does not
public A get();
public B get();
Yep. Bytecode relies on that in fact for bridge methods of certain types
bytecode uses name+desc for unique key, on both fields and methods
As in, a class implementing Supplier<String> will have a String get() and an Object get() in bytecode
did someone say bridge methods? looks at https://library.sciwhiz12.tk/java/bridge/
yay bridge methods ._.
speaking of bytecode trivia, did you know that record accessors/hashcode/equals are the only methods generated by javac which do not have the synthetic flag
bug
technecally a JLS spec violation
cant cite, but ifaik the jls specifies that all members that do not exist in source must have the synthetic flag
those don't have the synthetic flag? Weird
yep, had to special case in my decompiler, becuase, javac never makes it easy ._.
looked online, and it seems it's noted in JLS 13.1 (the cinary compatibility chapter)
- A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).
there you go
I mean, record implies having equals, hash and accessors
That's literally the purpose of records
Seems like it's in spec to me.
construct declared explicitly or implicitly in source code
That's what I said
the record methods are implicit
i can argue that the accessors are implicit due to the components, but the equals/hash/tostring, nah
actually, this SO page I'm reading raises that same argument, too
with a reference to the JLS as well
JLS 8.10.3
A record class provides implementations of all the
abstractmethods declared in classRecord. For each of the following methods, if a record classRdoes not explicitly declare a method with the same modifiers, name, and signature (§8.4.2), then the method is implicitly declared as follows:
[... definitions forequals,hashCode,toString...]
If a record class has a record component for which an accessor method is not declared explicitly, then an accessor method for that record component is declared implicitly, with the following properties:
[...]
so it's well within the spec 
it's surprisingly not a runtime problem - the refs are always hardcoded - so the bytecode always knows what you're talking about. There's not really a "virtual" lookup table (there is, but it has special calling semantics). so in general, if you add a new name in a subclass, it'll just be ignored, invisible. this is actually already a "problem" in modding, shadowing fields and methods and stuff. mixin has a whole slew of special stuff to try and help handle it.
oh hey, even the JVMS18 explicitly excludes those record members from being marked as synthetic (whether by Attribute or by access flag)
JVMS section 4.7.8 (the Synthetic Attribute)
[...] A class member that does not appear in the source code must be marked using a
Syntheticattribute, or else it must have itsACC_SYNTHETICflag set. The only exceptions to this requirement are compiler-generated members which are not considered implementation artifacts, namely:
- an instance initialization method representing a default constructor of the Java programming language (§2.9.1)
- a class or interface initialization method (§2.9.2)
- the implicitly declared members of enum and record classes (JLS §8.9.3, JLS §8.10.3)
now i'm curious if, if you make a private method protected (or public) and override it in a subclass, will its callers actually call your override
i'd always assumed so
It does actually do a virtual lookup super lookup for fields:
public class EggSample {
public String getString(B b) {
return b.aField;
}
public static class A {
public String aField;
}
public static class B extends A{
}
}
// access flags 0x1
public getString(LEggSample$B;)Ljava/lang/String;
L0
LINENUMBER 7 L0
ALOAD 1
GETFIELD EggSample$B.aField : Ljava/lang/String;
ARETURN
L1
Depends on your java version
Or rather the one it's compiled by
On new ones, yes
On old ones, it used INVOKESPECIAL for private methods, so no unless you have a tool to transform those to INVOKEVIRTUAL
Makes me wonder how ATs handled that then since a significant part of their use is to allow overriding previously private methods and that always worked AFAIK.
What does "old Java version" refer to in this case?
iirc bellow 1.6
ATs transform invokespecials to invokevirtuals when they refer to a previously-private (transformed into non-private) method, iirc
somewhere around there ACC_SUPER was enabled by default for classes and INVOKESPECIAL is now just to target specific implementations (skipping virtual table)
That's not a virtual lookup
That just looks in super classes or interfaces
A virtual lookup looks in subclasses
right, wrong word
But yeah, it checks inherited stuff
Ah, okay, that's long before I cared about how any of this worked 😄
you knew what i meant and i knew what i mean :)
Abstracting a field to a superclass is not a binary breaking change, luckily
(but not everyone else might have)
JVMS18, section 4.1
In Java SE 8 and above, the Java Virtual Machine considers the
ACC_SUPERflag to be set in everyclassfile, regardless of the actual value of the flag in theclassfile and the version of theclassfile.
Note that you can still use INVOKESPECIAL for private methods by using the -XDdisableVirtualizedPrivateInvoke flag
huh, wild
Though it will still use INVOKEVIRTUAL in cases where nested classes are involved I believe
iirc, lambda generated classes use IVNS, from an inner-class to call the private lambda impls
Yep, and nested classes
but i think INVS is just a semantic there, you could use INVV
Well, other way around
Lambdas and nested classes need INVV
They can't use INVS, I believe
The use of INVV for private methods happened at the same time as nest member stuff getting added
at first, I thought you made a typo with that JVM option because of the XD part, but it is actually typed that way
https://github.com/openjdk/jdk/blob/149acd186ed68d290e22dc2c86e17f46ef68b124/test/jdk/java/lang/invoke/SpecialInterfaceCall.java#L29
Hmm, no actually it kinda depends, yeah, i think the lambda callsite can specify
yes. we do
Speaking of things the runtime doesn't give a shit about: you can AT record elements or otherwise inject non-final fields into records and the JVM doesn't care that they technically break the entire contract of a record 😄
Doesn't it use MethodHandles nowadays, rather than INVOKESPECIAL?
yeah. we should probably protect that, because that could be quite breaking
Now, this does lead to a potential runtime issue with ATs, especially in Mojmaps where you don't have srg names helping you:
- Minecraft defines method a in class A which is private
- mod extends that class with its own method a, which is public or whatever, and is unaware that the super method exists as it's private
- ...another mod ATs the super method
that can cause issues
you should be able to do the same with mixin
The generated class still needs instructions
and tbh i'd argue that if mixin can inject fields into records it's a feature and not a bug 😛
But yeah, the actual lambda invocation is INDY
The "injecting fields" part was refering to exactly that because I recently had to do such a heretic thing 😛
i don't think this is a real problem. but please build a POC if you can
Yeah - the generated class receives the method handle as a dynamic constant, and then calls .invokeExact on it.
...I'm actually now unreasonably paranoid about this happening with Mojmaps now. Is there any way around it besides "hope people don't do that"?
test case please
Will do
to echo cpw, I would like to see a test case too
I can't actually do that without runtime Mojmaps though
you can build mocks pretty easily.
Heck wait I can just use srg names
perhaps a light env with the AT engine?
you can do it with a method that just so happens to have a method name thats an srg though
It'll look weird but it'll still work
to 'simulate' runtime moj
Yeah, I'll do that
in practice, i dont think it will actually occurr, the likely hood of someone implementing a function with the same signature and somehow turning into a virtual lookup will be very low
So INVS then, since that's what invokeExact is I believe
especially on the classes that mods commonly implement, most functions are public or static private/public
Hey @fallow sundial can your tool list the most commonly ATed methods?
Because if so that would help us see how likely it is someone overlaps with one of those just based on whether they're in things you tend to extend and how generic the name is
no not right now
i don't think this is a real issue. that's why i want to see what you get in a mock. i think you'll find that the "ATed" subclass method is just ignored.
public static class MojangClassA {
private String getMyString() {
return "MojangClassA";
}
public void doThing() {
System.out.println(getMyString());
}
}
public static class ModClassB extends MojangClassA {
public String getMyString() {
return "ModClassB";
}
public void doThing() {
super.doThing();
System.out.println(getMyString());
}
}
will print:
MojangClassA
ModClassB
If we apply public MojangClassA.getMyString ()Ljava/lang/String;
it will print:
ModClassB
ModClassB
thats the case luke is describing
@mental carbon
https://github.com/neoforged/NeoForm/actions/runs/6202061514/job/16840303448#step:9:160
Got around to testing it:
Install just firstmod. Play the game. Give yourself a firstmot:example_item (from the tools tab not the creative menu); you can right click it to "blow" it without issue (it's an instrument item, like goat horns). Now install just secondmod. Nothing much should happen except some logging during game start. Now install both, give yourself that item (once again from the creative menu), and attempt to blow it:
java.lang.ClassCastException: class net.minecraft.resources.ResourceLocation cannot be cast to class net.minecraft.core.Holder
@cpw example of this; it uses SRG names so the bit in firstmod looks a bit ugly; obviously would be cleaner in mojmaps. That was just the first private method I could find and easily write a test mod for - I highly suspect that any issues we see with this will come out ofEntityand company, especially some of the subclasses, which just have a lot of methods. Not sure how much of an issue this is, but if it's a real worry, we could make FG run a check on jars and error on any classes that contain methods that have the same signature as a private superclass method
... @frail oriole cause my ping didn't want to work
firstmod is the one with the badly named method, secondmod has the AT
The class I did this with is InstrumentItem
Let me bundle up the source quickly and dump that here too
Here's the source
The worst part is that the stack trace doesn't mention either of the mods in question, at any point in time whatsoever
TL;DR it's easy to come up with a case where this could break; I expect such cases to be very rare but in the huge number of combinations of different mods out there maybe we'll see one and this'll help us figure out what an otherwise unintelligible stack trace means
But if anyone asks why I prefer accessors over ATs I will now point them at this
This sort of bug, if it actually pops up, would be very hard to diagnose; my example here causes a crash but this is also the sort of thing that could have, like, silent behavioural changes. I'm actually pretty tempted to see how hard it would be to make FG enforce stuff so this isn't possible
I'm also going to build a small tool, if I can, to make sure there's nothing in vanilla where vanilla potentially does this to itself
Ok. So it's as I said. The override is invisible in runtime and could cause secondary issues but isn't a direct crash. Checking is obviously worth it during mod building. But runtime checks are gonna be expensive and slow
This could be an additional issue with mojmaps vs srg, but it's not really a huge change
It can cause crashes directly due to generics though, if the return types (or, heck, parameter types) have the same raw type but different generics. I agree, runtime checking is... not really an option. A compile-time check to just stop people from doing anything that could lead to this seems like the smartest option
Yeah, if you can figure it out. I think it's just something to be cognizant of.
Could probably be embedded in the remapping system?
I'll have to poke the relevant bits. I'll probably wait till NGNext exists unless it looks like it's going to be separate from that chunk of stuff
My biggest worry isn't "this'll happen all the time", but "if this does happen it's borderline impossible to diagnose in basically every case it'll happen in, whether it's a crash or a silent issue"
Because there's... nothing, at all, to clue you in on what the culprit is 9 times out of 10, with the possible exception of "an item from this mod caused it" which is only really relevant in cases like this where it's a nice, obvious item
Is this something that could be possible in the future, or is this a "that's pretty difficult" sort of thing?
But yeah, I suspect this sort of check could be embedded in remapping, I just have to see how that system works
...fuck nevermind we won't be re-obfing mods anymore will we, which is actually great, just bad for this particular thing
I am also nuking the naming services
Cause they are a massive overhead on the naming channels
Lock us in into a legacy format
And are not usefull anymore
Hmm. As the gradle expert, do you have any ideas as to how plausible such a compile time check would be?
It's pretty trivial to do (with one exception: wildcard ATs
), I quickly threw a chart view into my tool which already had an AT extractor component, I just need to make the labels not fuck up the graph width
Can I get a TLDR or opne specitically?
on what*
specifically**
Jesus
My typing
Basically, making sure that no class has a method that has the same name and descriptor as a private method in a superclass/interface that is a valid AT target
That's what I figured; I was wondeirng if NG already did any sort of ASM checks/modification passes that would be easy to hook into for stuff like this
Sadly that is however not something you should be preventing
AT'ing private methods which are in none final classes is not allowed by definition
Because of that exact reason
If they're ATed they're no longer private
Yeah
So the check won't fail for the override of them
But any inherting class could have the same method and signature in them
As far as I know this is a problem at runtime
Cause it can create completely unintended behaviour
Yes it can. Hence why you want to run that check
Due to one mod making a method protected
See: #1136320550168436798 message
I know, but that does not need to be a check
Simply prevent the AT from being applied at runtime
And show a nice error message
And bob is your uncle
It's not an issue with SRG but it'll be an issue with mojmaps
And how do you do that?
*efficiently, that is
You load a vanilla class with an AT to a private method
How do I know whether that AT is safe to apply?
Exactly. It has to be a compile time check.
Any attempt to AT a private method in a none final class is by definition not safe
Not even your compile time check guards against that
Orion can you fix the issue
It kinda does though
The compile time check works fine, with the exception of mods that bypass the compile time check
Such a compile time check would prevent this issue
So you're telling me that one shouldn't use ATs for the one thing where ATs are actually the only solution? That's nonsense
I don't understand the issue, you requested that I did not remap the constructor parameters
So I did not
There is no solution.
Period
End of discussion, because no matter the compile time a check
Once again, I don't see why not?
Because there is no way to protect the runtime from undefined behaviour
The simplest solution
The constructor parameters need to be remapped to the field names provided by mojmap
Which I did
They are a, b, c, d, e, f, g, h etc
Or do you mean their actuall names
Ahhh
Hold on then
And it is possible, with a compile time check, to make it so that the only possible issues come from mods that bypass the compile time check. That is trivial
Will patch that
Well, by "trivial" I mean the "how to do it" is. The actual doing would be an ASM run over the jar
What exactly do you want to prevent?
#1136320550168436798 message
[Reference ➤ ](#1136320550168436798 message)Got around to testing it:
Install just firstmod. Play the game. Give yourself a firstmot:example_item (from the tools tab not the creative menu); you can right click it to "blow" it without issue (it's an instrument item, like goat horns). Now install just secondmod. Nothing much should happen except some logging during game start. Now install both, give yourself that item (once again from the creative menu), and attempt to blow it:
java.lang.ClassCastException: class net.minecraft.resources.ResourceLocation cannot be cast to class net.minecraft.core.Holder
@cpw example of this; it uses SRG names so the bit in firstmod looks a bit ugly; obviously would be cleaner in mojmaps. That was just the first private method I could find and easily write a test mod for - I highly suspect that any issues we see with this will come out ofEntityand company, especially some of the subclasses, which just have a lot of methods. Not sure how much of an issue this is, but if it's a real worry, we could make FG run a check on jars and error on any classes that contain methods that have the same signature as a private superclass method
[Reference ➤ ](#1136320550168436798 message)Now, this does lead to a potential runtime issue with ATs, especially in Mojmaps where you don't have srg names helping you:
- Minecraft defines method a in class A which is private
- mod extends that class with its own method a, which is public or whatever, and is unaware that the super method exists as it's private
- ...another mod ATs the super method
that can cause issues
Yeah
You can do that at compile time
So you want a validator for ATs?
You don't need to know that the other mod with the AT exists
Do you want a simple check that runs through all ATs and barfs if you add one for a private method in a none final class?
It's validating classes that re-use names/descriptors that could be ATed in vanilla
No
You can't do that!
This checker never looks at ATs
That behaviour is JVM valid!
But not mod valid
The AT is the magic runtime tool
And mods should adapt to it
It is impossible to code it as such that it never breaks the JVM
Mods which are doing legally valid java with forge, without a single coremod or AT
Should NEVER have to adapt their code
To mods that work with ATs, mixins or coremods
That is my opinion
And I refuse to help people which are trying to achieve otherwise
Simply said if you want to do this, fine
Make a tool that checks the compiled jars
It is trivial with ASM to do
Create a task that invokes the tool with the relevant jars as input
And throws an exception if the tools finds errors
At that point you might as well delete ATs and tell people to use accessor and invoker mixins, because that would be the only thing ATs can be used for then. You would be breaking absurd amounts of mods with such a rule
I'm a bit confused what the alternative is to a compile time check? If this issue happens at runtime it is impossible to diagnose in many cases
that's kinda my point too orion. this is a thing people will do sometimes.
you have to build a specifically pathological case to make it break the way luke did.
Like, this is a thing that could, genuinely, happen very easily with the switch to mojmaps
You can literally show an error message:
`Hello mod X is trying to apply an AT which is attempting to make a private method in a none final class protected/public. This is not allowed." -> Do not start the game
If we're switching to runtime mojmaps, we have a responsibility to make sure that it doesn't because of the severity of it if it does happen, and how difficult it is to diagnose
because don't forget, the modder sees the ATed MC and neo.
if they apply an AT, they see it.
I had no compiler warnings in my setup?
We're talking two different mods
The problem is two mods cpw
ok
How do you detect that? Logistically. At runtime. Cause the approaches I can see all involve rather expensive stuff
both are perfectly legal java
And one that is using an AT to do something that is normally not legal in java
Because the method is private
and we cannot implement a runtime check, because that's stupidly expensive. inheritance checks are
IMHO, the mod that is doing nothing wrong, should not be the one that needs to be adapted.
personally i'd argue its on the mod with the AT to validate that what they're doing is unlikely to break things
but in practice the average modder probably lacks an understanding of bytecode at that level 🙃
The check is trivial: No AT can be used to make a private method public if it is in a none final class
That is a trivial check to make
And costs little to no CPU time
Unfortunately the only way to verify that it won't break stuff is "never AT a private method in a non-final class". And then, heck, "never AT a private instance method at all" because people can un-final classes with ATs
Hell we could technically even show an error during project setup
And not allow the AT at all
If we just fail the access transformer tool with such an error message
So you're saying "butcher ATs"
since we have mixins this doesn't really limit anything, but does require code adaptation
Well yes that is the concequence
the question is: does this occur often enough in practice that we care
I don't know
It does limit stuff. ATs of that sort are useful when you want a subclass to override a private method. The only other optoin is "inject at the head and do an instanceof check" which doesn't work if you're expecting other people to further subclass your stuff
But I don't think that a completely legal mod, needs to adapt and change its code, because another mod is doing something agains the java compiler rules
The issue is, basically, if it did occur in practice we'd have no way to know that this was the issue, due to the nature of the issue caused
but that makes AT effectively useless.
Like, logs don't implicate either mod involved, etc
It can still make fields public, make methods in final classes public etc
It can make protected methods public
You seem to think ATs aren't "perfectly valid mods". That is just plain wrong. This is the modded world. They are
is it not possible to warn (not crash) for this at runtime in the classloader?
It just can do anything to private methods
mixin isn't a solution
ATs are perfectly valid mods
With very costly inheritance checks, yes. But it would slow stuff down a lot
Mods without ATS are just more perfectly valid
which is about 0.0000000000000000000000000000000000000000000000000000000000000000000000000001% of it's uses
I know
i'm skeptical that the check would be that costly
IMO we should just ignore it
I would ignore it too
inheritance checks are EXPENSIVE.
trust me. the speed up when we removed a few was NOTICEABLE
Okay, so, you're loading a class. You need to check every super class and super interface of the class for any instance methods that share names with methods of the class being loaded, check if an AT has been applied to any of those fields from private to whatever, and if it has error/warn/whatever. For every class you load.
I mean if we add this compile check
if none of the superclasses were ATed, we can bail early
Yes
btw: you have to do this while classloading, which means you can easily get into circularity fun as well
it's NOT cheap, and i will veto any solution that requires doing this.
Let me put it this way orion: the number of cases where this occurs at runtime should be comparable to the number of mods that would be affected by my compile time check. If you're not worreid about this happening at runtime, you shouldn't care about the compile time check because it'll never catch anything
IMO, this is NOT a serious issue. It's a possible problem, YES. but it's one in a crowded field of thousands.
And a modder does add his own version of the method which is private in the super class, which is completely legal in java to do, there is nothing wrong with that. And we will then tell that modder: Great you are using forge and java to its fullest potential, but sorry for you, there are modders which do something that is normally illegal in java and you break their mods, as such you are not allowed to do this?!?
It's a problem that we won't notice if its happening
won't we? we know pretty much everything here.
in theory an external transformation service could be used to detect it (provided that it can be run after ATs)
it might be difficult to diagnose directly.
Yes? I mean we already do that no? With the use of runtime generics for event bus and all that shit...
Not that is different, that is again at runtime etc
We do nothing at compile time
On purpose
By design
If the compiler allows it
It is fair game
we have three choices:
- ignore it. if it becomes an issue, we might have to think again.
- drop mojmap. We break the whole world when we create conflicting srg.
- never release 20.2.
pick
now
I vote for 1)
...that's not at all what I have been saying
And frankly I'm a bit insulted that you'd imply that
I'm saying that we should build a compile time checker to prevent this issue
I think that the switch to mojmaps is essential
I remain skeptical that option 4 (runtime check) is costly
Well no cpw, he argues taht we need to prevent modders from making private methods with the same name and signature as private method in their super type
runtime check is too costly without evidence of a real issue
which is effectively "remove AT"
this is effectively "remove AT" if it barfs on all those scenarios
hmmm
It actually does not complain about the AT
yeah
You're basically saying that mods that don't use ATs are "more correct" and "more normal" than mods that do. This is not, to my awareness, an official neoforge policy. As such, I will take what you are saying as your own opinion and not the opinion of neoforge.
it tells off the normal modder, not the problematic modder.
since when are ATs problematic
that's the problem tho. the possible actions of one modder affect the entire space.
It barfs at the normal modder, which is doing something completely legal, that he is potentially, going to break a users runtime if the user installs a problematic mod
and you're saying that we need to warn the entire space of modders because of that.
In this case they are, in general they are not
They are currently what the issue is
it's not about problematic or not.
And frankly I'm tired of you guys making straw man arguments out of this. I'm not saying that this is some huge flaw with ATs, and I'm not saying it should hold up mojmaps or whatever, I'm just saying that this is an issue that is subtle enough and severe enough that we should proactively prevent it
I see
Because they circumvent the normal protections of the client
it's that we're wanting to bake in a warning to everyone that "someone could break this"
I understand luke
may as well just make that warning output all the time. "warning, your method might be mixined at runtime!"
that's not helpful
it's part of the territory of modding.
Here's an idea: the compile time checker, instead of erroring, writes the potentially problematic stuff to a file
i would probably want to think twice even as a "normal" modder if i am overriding a mojang class, and use the same method name (private or not)
That file is bundled into the jar
And forge logs its contents at runtime
That way, the information is there
So the problem, if it occurs, isn't as "hidden"
Heck, forge could match up ATs and contents of that file at runtime
Actually luke that might not be a bad idea
Something to the extend of the annotation metadata
Just for those kinds of inheritted methods
a compilation logger might be good
You've got a compile time check, that makes the runtime check cheap
but it's gonna be for everything
this sounds like it eliminates most overhead of the check
because it's NOT JUST AT
You can log a warning in dev, and an error at runtime
mixin has all the same potential problems
Mixin actually does not
yeah it can
Mixin cannot change method visibility
So someone has to be intentional to mess with stuff
no, but it can completely change the behaviour of an internal method
thus causing invisible problems 😛
thats actually not 100% correct, I think overwrite can
arguably so can coremods.
Yes, but luckily we already log a bunch of info about what mixins are present
Ah, lovely
it's part of the ecosystem of mods
Yep, and coremods too
we log a bunch of info about ATs and coremods too 😛
The issue is that ATs are generally thought of as "safe" and it's not obvious that it could break stuff. Unlike, say, mixins and coremods where its really goddamn obvious
But it can create conflicting methods with accessors and invokers, especially the former, if people use automatic target deduction instead of namespacing the method and setting the target in the annotation
IMHO, we should put this on hold, but luke could you do me a favor and go to the NG repo, and open an issue
Additionally, mixins shove info about the mods involved in stack traces
@mental carbon did you make sure that s2s is run with j17 using toolchains?
