Skip to content

Commit

Permalink
feat: fakedeath module + player.respawn()
Browse files Browse the repository at this point in the history
  • Loading branch information
Misat11 committed Jan 13, 2024
1 parent acff9ae commit 9a84a8d
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ public void showPlayer(@NotNull Player player) {
return ((org.bukkit.entity.Player) wrappedObject).getAddress();
}

@Override
public void respawn() {
((org.bukkit.entity.Player) wrappedObject).spigot().respawn();
}

@SuppressWarnings("unchecked") // Via Version
@Override
public int getProtocolVersion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ public interface Player extends Sender, OfflinePlayer, HumanEntity, PlayerAudien
*/
@Nullable InetSocketAddress getAddress();

/**
* Respawns the player.
*/
void respawn();

/**
* Launches the player in its facing direction.
*
Expand Down
2 changes: 0 additions & 2 deletions extensions/ai/bukkit/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
dependencies {
api project(':core-bukkit')

compileOnly libs.paper
}
4 changes: 1 addition & 3 deletions extensions/ai/common/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
dependencies {
api project(':core-common')
}
includeModule('core')
3 changes: 3 additions & 0 deletions extensions/fakedeath/bukkit/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compileOnly libs.paper
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package org.screamingsandals.lib.impl.bukkit.fakedeath;

import org.bukkit.*;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.screamingsandals.lib.Server;
import org.screamingsandals.lib.impl.bukkit.utils.nms.ClassStorage;
import org.screamingsandals.lib.impl.nms.accessors.CombatTrackerAccessor;
import org.screamingsandals.lib.impl.nms.accessors.ComponentAccessor;
import org.screamingsandals.lib.impl.nms.accessors.LivingEntityAccessor;
import org.screamingsandals.lib.impl.nms.accessors.PlayerAccessor;
import org.screamingsandals.lib.impl.nms.accessors.ServerPlayerAccessor;
import org.screamingsandals.lib.utils.annotations.Service;
import org.screamingsandals.lib.utils.reflect.Reflect;
import org.screamingsandals.lib.world.Location;
import org.screamingsandals.lib.fakedeath.FakeDeath;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;

@Service
public class BukkitFakeDeath extends FakeDeath {
@Override
protected void die0(@NotNull org.screamingsandals.lib.player.Player slibPlayer, @NotNull FakeDeath.PlayerInventoryLifeResetFunction function) {
var player = slibPlayer.as(Player.class);
if (player.isDead()) {
return;
}

var loot = new ArrayList<ItemStack>();
Collections.addAll(loot, player.getInventory().getContents());
loot.removeIf(Objects::isNull); // remove nulls;

var deathWorld = player.getWorld();
var deathLoc = player.getLocation();

String message = null;
try {
var combatTracker = Reflect.fastInvoke(ClassStorage.getHandle(player), LivingEntityAccessor.getMethodGetCombatTracker1());
var component = Reflect.fastInvoke(combatTracker, CombatTrackerAccessor.getMethodGetDeathMessage1());
message = (String) Reflect.fastInvoke(component, ComponentAccessor.getMethodFunc_150254_d1());
} catch (Throwable ignored) {}

var event = new PlayerDeathEvent(player, loot, player.getTotalExperience(), 0, message);
Bukkit.getServer().getPluginManager().callEvent(event);

for (var stack : event.getDrops()) {
if (stack == null || stack.getType() == Material.AIR) continue;

deathWorld.dropItem(deathLoc, stack);
}

player.closeInventory();

var deathMessage = event.getDeathMessage();
if (deathMessage != null && !deathMessage.trim().equals("") && Boolean.parseBoolean(deathWorld.getGameRuleValue("showDeathMessages"))) {
Bukkit.broadcastMessage(deathMessage);
}

// TODO: find better way how to broadcast this effect and don't break the game

/*
try {
Reflect.getMethod(ClassStorage.getHandle(deathWorld), "broadcastEntityEffect,func_72960_a", ClassStorage.NMS.Entity, byte.class)
.invoke(ClassStorage.getHandle(player), (byte) 3);
} catch (Throwable t) {
}
*/

// ignoring PacketPlayOutCombatEvent, client probably didn't know that he died

try {
Reflect.fastInvoke(ClassStorage.getHandle(player), PlayerAccessor.getMethodRemoveEntitiesOnShoulder1());
} catch (Throwable ignored) {}

if (Server.isVersion(1, 16)) {
try {
Boolean b = deathWorld.getGameRuleValue(GameRule.FORGIVE_DEAD_PLAYERS);
if (b != null && b) {
Reflect.fastInvoke(ClassStorage.getHandle(player), ServerPlayerAccessor.getMethodTellNeutralMobsThatIDied1());
}
} catch (Throwable ignored) {}
}

int i = event.getDroppedExp();
while (i > 0) {
int j = getOrbValue(i);
i -= j;
((ExperienceOrb) deathWorld.spawnEntity(deathLoc, EntityType.EXPERIENCE_ORB)).setExperience(j);
}

function.reset(slibPlayer, event.getKeepInventory());
try {
player.setKiller(null);
player.setLastDamageCause(null);
} catch (Throwable ignored) {}

if (event.getKeepLevel() || event.getKeepInventory()) {
player.setTotalExperience(event.getNewTotalExp());
player.setLevel(event.getNewLevel());
player.setExp(event.getNewExp());
}

try {
Reflect.fastInvoke(ClassStorage.getHandle(player), ServerPlayerAccessor.getMethodSetCamera1(), ClassStorage.getHandle(player));
} catch (Throwable ignored) {}

try {
Object combatTracker = Reflect.fastInvoke(ClassStorage.getHandle(player), LivingEntityAccessor.getMethodGetCombatTracker1());
Reflect.fastInvoke(combatTracker, CombatTrackerAccessor.getMethodRecheckStatus1());
} catch (Throwable ignored) {}

// respawn location will be changed by PlayerListener
var respawnEvent = new PlayerRespawnEvent(player, player.getLocation(), false);
Bukkit.getServer().getPluginManager().callEvent(respawnEvent);

slibPlayer.teleport(Location.fromPlatform(respawnEvent.getRespawnLocation()));
}

public int getOrbValue(int i) {
if (i > 162670129) return i - 100000;
if (i > 81335063) return 81335063;
if (i > 40667527) return 40667527;
if (i > 20333759) return 20333759;
if (i > 10166857) return 10166857;
if (i > 5083423) return 5083423;
if (i > 2541701) return 2541701;
if (i > 1270849) return 1270849;
if (i > 635413) return 635413;
if (i > 317701) return 317701;
if (i > 158849) return 158849;
if (i > 79423) return 79423;
if (i > 39709) return 39709;
if (i > 19853) return 19853;
if (i > 9923) return 9923;
if (i > 4957) return 4957;
return i >= 2477 ? 2477 : (i >= 1237 ? 1237 : (i >= 617 ? 617 : (i >= 307 ? 307 : (i >= 149 ? 149 : (i >= 73 ? 73 : (i >= 37 ? 37 : (i >= 17 ? 17 : (i >= 7 ? 7 : (i >= 3 ? 3 : 1)))))))));
}
}
1 change: 1 addition & 0 deletions extensions/fakedeath/common/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
includeModule('core')
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2024 ScreamingSandals
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.screamingsandals.lib.fakedeath;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.screamingsandals.lib.Core;
import org.screamingsandals.lib.player.Player;
import org.screamingsandals.lib.utils.Preconditions;
import org.screamingsandals.lib.utils.annotations.AbstractService;
import org.screamingsandals.lib.utils.annotations.ServiceDependencies;

@AbstractService("org.screamingsandals.lib.impl.{platform}.fakedeath.{Platform}FakeDeath")
@ServiceDependencies(dependsOn = Core.class)
public abstract class FakeDeath {
private static @Nullable FakeDeath fakeDeath;

@ApiStatus.Internal
public FakeDeath() {
Preconditions.checkArgument(fakeDeath == null, "FakeDeath is already initialized.");
fakeDeath = this;
}

public static void die(@Nullable Player player, @NotNull FakeDeath.PlayerInventoryLifeResetFunction function) {
if (player == null) {
return;
}
Preconditions.checkArgument(fakeDeath != null, "FakeDeath is not initialized yet.");
fakeDeath.die0(player, function);
}

protected abstract void die0(@NotNull Player player, @NotNull FakeDeath.PlayerInventoryLifeResetFunction function);

@FunctionalInterface
public interface PlayerInventoryLifeResetFunction {
void reset(@NotNull Player player, boolean keepInventory);
}
}
9 changes: 9 additions & 0 deletions nms/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ nmsGen {
reqMethod('getAbsorptionAmount')
reqMethod('setAbsorptionAmount', float)
reqMethod('getAttributes')
reqMethod('getCombatTracker')
}

reqClass('net.minecraft.world.entity.Mob') {
Expand All @@ -123,6 +124,11 @@ nmsGen {
reqClass('net.minecraft.world.entity.PathfinderMob')
// end 1.8.8 compat

reqClass('net.minecraft.world.damagesource.CombatTracker') {
reqMethod('getDeathMessage')
reqMethod('recheckStatus')
}

reqClass('net.minecraft.network.chat.Component$Serializer') {
reqMethod('searge:func_150699_a', String) // up to 1.15.2
reqMethod('searge:m_130701_', String) // 1.16.1+
Expand Down Expand Up @@ -276,11 +282,14 @@ nmsGen {

reqClass('net.minecraft.world.entity.player.Player') {
reqMethod('attack', '@Entity')
reqMethod('removeEntitiesOnShoulder')
}

reqClass('net.minecraft.server.level.ServerPlayer') {
reqField('connection')
reqField('latency')
reqMethod('setCamera', '@Entity')
reqMethod('tellNeutralMobsThatIDied')
}

reqClass('net.minecraft.world.entity.projectile.FishingHook')
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ registerExtension('placeholders', 'bukkit') //TODO: add others
registerExtension('economy', 'bukkit')
registerExtension('packets', 'vanilla', 'bukkit') //TODO: add others
registerExtension('ai', 'bukkit')
registerExtension('fakedeath', 'bukkit')
registerExtension('cloud', 'bukkit', /*'minestom' , 'sponge',*/ 'bungee', 'velocity', 'extras')

0 comments on commit 9a84a8d

Please sign in to comment.