Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Commit 929bf8d

Browse files
authored
Implement the LivingDropsEvent event (#96)
* Add the IForgeEntity/Entity method captureDrops, and implement entity drop capturing * Add LivingDropsEvent * Fix the Javadoc for LivingDropsEvent * Switch the LivingDropsEvent sender to using a ThreadLocal * Fix the Javadoc for MixinLivingEntity.dropLootingLevel mentioning atomic references
1 parent e1cd10e commit 929bf8d

File tree

7 files changed

+299
-0
lines changed

7 files changed

+299
-0
lines changed

patchwork-events-entity/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ version = getSubprojectVersion(project, "0.4.0")
33

44
dependencies {
55
compile project(path: ':patchwork-fml', configuration: 'dev')
6+
compile project(path: ':patchwork-extensions', configuration: 'dev')
67
compile project(path: ':patchwork-extensions-item', configuration: 'dev')
78
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.minecraftforge.event.entity.living;
21+
22+
import java.util.Collection;
23+
24+
import net.minecraftforge.common.MinecraftForge;
25+
26+
import net.minecraft.entity.ItemEntity;
27+
import net.minecraft.entity.LivingEntity;
28+
import net.minecraft.entity.damage.DamageSource;
29+
30+
/**
31+
* LivingDropsEvent is fired when an Entity's death causes dropped items to appear.
32+
*
33+
* <p>This event is fired whenever an Entity dies and drops items in {@link LivingEntity#onDeath(DamageSource)}.</p>
34+
*
35+
* <p>
36+
* {@link #source} contains the {@link DamageSource} that caused the drop to occur.<br>
37+
* {@link #drops} contains the Collection of {@link ItemEntity}s that will be dropped.<br>
38+
* {@link #lootingLevel} contains the level of Looting used to kill the entity doing the drop.<br>
39+
* {@link #recentlyHit} determines whether the entity doing the drop has recently been damaged by a player or tamed wolf.<br>
40+
* </p>
41+
*
42+
* <p>This event is cancelable. If this event is canceled, the Entity does not drop anything.</p>
43+
*
44+
* <p>This event does not have a result.</p>
45+
*
46+
* <p>This event is fired on the {@link MinecraftForge#EVENT_BUS}.</p>
47+
*/
48+
public class LivingDropsEvent extends LivingEvent {
49+
private final DamageSource source;
50+
private final Collection<ItemEntity> drops;
51+
private final int lootingLevel;
52+
private final boolean recentlyHit;
53+
54+
public LivingDropsEvent(LivingEntity entity, DamageSource source, Collection<ItemEntity> drops, int lootingLevel, boolean recentlyHit) {
55+
super(entity);
56+
this.source = source;
57+
this.drops = drops;
58+
this.lootingLevel = lootingLevel;
59+
this.recentlyHit = recentlyHit;
60+
}
61+
62+
public DamageSource getSource() {
63+
return source;
64+
}
65+
66+
public Collection<ItemEntity> getDrops() {
67+
return drops;
68+
}
69+
70+
public int getLootingLevel() {
71+
return lootingLevel;
72+
}
73+
74+
public boolean isRecentlyHit() {
75+
return recentlyHit;
76+
}
77+
78+
@Override
79+
public boolean isCancelable() {
80+
return true;
81+
}
82+
}
83+

patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,23 @@
1919

2020
package net.patchworkmc.mixin.event.entity;
2121

22+
import java.util.ArrayList;
23+
import java.util.Collection;
24+
25+
import net.minecraftforge.common.MinecraftForge;
26+
import net.minecraftforge.common.extensions.IForgeEntity;
27+
import net.minecraftforge.event.entity.living.LivingDropsEvent;
2228
import org.spongepowered.asm.mixin.Mixin;
29+
import org.spongepowered.asm.mixin.Shadow;
2330
import org.spongepowered.asm.mixin.Unique;
2431
import org.spongepowered.asm.mixin.injection.At;
2532
import org.spongepowered.asm.mixin.injection.Inject;
2633
import org.spongepowered.asm.mixin.injection.ModifyVariable;
2734
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
2835
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
36+
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
2937

38+
import net.minecraft.entity.ItemEntity;
3039
import net.minecraft.entity.LivingEntity;
3140
import net.minecraft.entity.damage.DamageSource;
3241

@@ -37,6 +46,9 @@ public class MixinLivingEntity {
3746
@Unique
3847
private float[] fallData;
3948

49+
@Shadow
50+
protected int playerHitTimer;
51+
4052
// TODO: Forge bug: PlayerEntity calls its super, so this event gets fired twice on the client.
4153
@Inject(method = "onDeath", at = @At("HEAD"), cancellable = true)
4254
private void hookDeath(DamageSource source, CallbackInfo callback) {
@@ -101,6 +113,56 @@ private void hookHandleFallDamageCancel(float distance, float damageMultiplier,
101113
}
102114
}
103115

116+
/**
117+
* Carry over the looting level between the two mixins for the drop method.
118+
*
119+
* <p>
120+
* The drop method has roughly this effect:
121+
*
122+
* <pre>{@code
123+
* protected void drop(DamageSource source) {
124+
* int lootingLevel = ...
125+
* // hookDropForCapturePre mixin
126+
* this.dropEquipment(source, lootingLevel, bl);
127+
* // FRAME CHOP 3
128+
* this.dropInventory();
129+
*
130+
* // Added by forge, MUST be called after dropInventory:
131+
* sendEvent(lootingLevel);
132+
* }
133+
* }</pre>
134+
*
135+
* And thus we can't access the looting level from the end since it's local has
136+
* been discarded. Thus we store it in a ThreadLocal, to keep track of it between
137+
* the two methods.
138+
* </p>
139+
*/
140+
@Unique
141+
private final ThreadLocal<Integer> dropLootingLevel = new ThreadLocal<>();
142+
143+
@Inject(method = "drop", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/LivingEntity;playerHitTimer : I"), locals = LocalCapture.CAPTURE_FAILHARD)
144+
private void hookDropForCapturePre(DamageSource src, CallbackInfo info, int lootingLevel) {
145+
IForgeEntity forgeEntity = (IForgeEntity) this;
146+
forgeEntity.captureDrops(new ArrayList<>());
147+
148+
dropLootingLevel.set(lootingLevel);
149+
}
150+
151+
@Inject(method = "drop", at = @At("TAIL"))
152+
private void hookDropForDropsEvent(DamageSource src, CallbackInfo info) {
153+
LivingEntity entity = (LivingEntity) (Object) this;
154+
IForgeEntity forgeEntity = (IForgeEntity) this;
155+
Collection<ItemEntity> drops = forgeEntity.captureDrops(null);
156+
157+
if (!MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, src, drops, dropLootingLevel.get(), playerHitTimer > 0))) {
158+
for (ItemEntity item : drops) {
159+
forgeEntity.getEntity().world.spawnEntity(item);
160+
}
161+
}
162+
163+
dropLootingLevel.remove();
164+
}
165+
104166
// No shift, because we are specifically not modifying the value for this function call.
105167
// TODO: Forge patches a bit later into the function here, being inconsistent with their patch for PlayerEntity. For the moment, I don't feel like finding an injection point for that, and this may be a Forge bug?
106168
@ModifyVariable(method = "applyDamage", argsOnly = true, at = @At(value = "INVOKE", target = "net/minecraft/entity/LivingEntity.setAbsorptionAmount (F)V", ordinal = 0))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.minecraftforge.common.extensions;
21+
22+
import java.util.Collection;
23+
24+
import javax.annotation.Nullable;
25+
26+
import net.minecraft.entity.Entity;
27+
import net.minecraft.entity.ItemEntity;
28+
29+
public interface IForgeEntity {
30+
default Entity getEntity() {
31+
return (Entity) this;
32+
}
33+
34+
@Nullable
35+
Collection<ItemEntity> captureDrops();
36+
37+
Collection<ItemEntity> captureDrops(@Nullable Collection<ItemEntity> captureDrops);
38+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin.extension;
21+
22+
import java.util.Collection;
23+
24+
import javax.annotation.Nullable;
25+
26+
import net.minecraftforge.common.extensions.IForgeEntity;
27+
import org.spongepowered.asm.mixin.Mixin;
28+
import org.spongepowered.asm.mixin.Unique;
29+
import org.spongepowered.asm.mixin.injection.At;
30+
import org.spongepowered.asm.mixin.injection.Redirect;
31+
32+
import net.minecraft.entity.Entity;
33+
import net.minecraft.entity.ItemEntity;
34+
import net.minecraft.world.World;
35+
36+
@Mixin(Entity.class)
37+
public class MixinEntity implements IForgeEntity {
38+
@Unique
39+
private Collection<ItemEntity> captureDrops = null;
40+
41+
@Redirect(method = "dropStack(Lnet/minecraft/item/ItemStack;F)Lnet/minecraft/entity/ItemEntity;",
42+
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
43+
private boolean hookDropStackForCapture(World world, Entity entity) {
44+
ItemEntity itemEntity = (ItemEntity) entity;
45+
46+
if (captureDrops() != null) {
47+
captureDrops().add(itemEntity);
48+
return true;
49+
} else {
50+
return world.spawnEntity(itemEntity);
51+
}
52+
}
53+
54+
@Nullable
55+
@Override
56+
public Collection<ItemEntity> captureDrops() {
57+
return captureDrops;
58+
}
59+
60+
@Override
61+
public Collection<ItemEntity> captureDrops(@Nullable Collection<ItemEntity> value) {
62+
Collection<ItemEntity> ret = captureDrops;
63+
this.captureDrops = value;
64+
return ret;
65+
}
66+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin.extension;
21+
22+
import net.minecraftforge.common.extensions.IForgeEntity;
23+
import org.spongepowered.asm.mixin.Mixin;
24+
import org.spongepowered.asm.mixin.injection.At;
25+
import org.spongepowered.asm.mixin.injection.Redirect;
26+
27+
import net.minecraft.entity.Entity;
28+
import net.minecraft.entity.ItemEntity;
29+
import net.minecraft.server.network.ServerPlayerEntity;
30+
import net.minecraft.world.World;
31+
32+
@Mixin(ServerPlayerEntity.class)
33+
public class MixinServerPlayerEntity {
34+
@Redirect(method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;",
35+
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
36+
private boolean hookDropItemForCapture(World world, Entity entity) {
37+
ItemEntity itemEntity = (ItemEntity) entity;
38+
IForgeEntity forgeEntity = (IForgeEntity) this;
39+
40+
if (forgeEntity.captureDrops() != null) {
41+
forgeEntity.captureDrops().add(itemEntity);
42+
return true;
43+
} else {
44+
return world.spawnEntity(itemEntity);
45+
}
46+
}
47+
}

patchwork-extensions/src/main/resources/patchwork-extensions.mixins.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
"package": "net.patchworkmc.mixin.extension",
44
"compatibilityLevel": "JAVA_8",
55
"mixins": [
6+
"MixinEntity",
67
"MixinEntityType",
78
"MixinEntityTypeBuilder",
9+
"MixinServerPlayerEntity",
810
"MixinStatusEffect"
911
],
1012
"injectors": {

0 commit comments

Comments
 (0)