mirror of
https://github.com/Minestom/Minestom.git
synced 2025-03-02 11:21:15 +01:00
Merge pull request #1 from jglrxavpok/master
Damage types and death messages
This commit is contained in:
commit
eaf9349613
@ -2,6 +2,7 @@ package fr.themode.demo;
|
|||||||
|
|
||||||
import fr.themode.demo.blocks.StoneBlock;
|
import fr.themode.demo.blocks.StoneBlock;
|
||||||
import fr.themode.demo.blocks.UpdatableBlockDemo;
|
import fr.themode.demo.blocks.UpdatableBlockDemo;
|
||||||
|
import fr.themode.demo.commands.GamemodeCommand;
|
||||||
import fr.themode.demo.commands.HealthCommand;
|
import fr.themode.demo.commands.HealthCommand;
|
||||||
import fr.themode.demo.commands.SimpleCommand;
|
import fr.themode.demo.commands.SimpleCommand;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
@ -32,6 +33,7 @@ public class Main {
|
|||||||
CommandManager commandManager = MinecraftServer.getCommandManager();
|
CommandManager commandManager = MinecraftServer.getCommandManager();
|
||||||
commandManager.register(new HealthCommand());
|
commandManager.register(new HealthCommand());
|
||||||
commandManager.register(new SimpleCommand());
|
commandManager.register(new SimpleCommand());
|
||||||
|
commandManager.register(new GamemodeCommand());
|
||||||
|
|
||||||
RecipeManager recipeManager = MinecraftServer.getRecipeManager();
|
RecipeManager recipeManager = MinecraftServer.getRecipeManager();
|
||||||
ShapelessRecipe shapelessRecipe = new ShapelessRecipe("test", "groupname") {
|
ShapelessRecipe shapelessRecipe = new ShapelessRecipe("test", "groupname") {
|
||||||
|
@ -7,6 +7,7 @@ import net.minestom.server.MinecraftServer;
|
|||||||
import net.minestom.server.benchmark.BenchmarkManager;
|
import net.minestom.server.benchmark.BenchmarkManager;
|
||||||
import net.minestom.server.benchmark.ThreadResult;
|
import net.minestom.server.benchmark.ThreadResult;
|
||||||
import net.minestom.server.entity.*;
|
import net.minestom.server.entity.*;
|
||||||
|
import net.minestom.server.entity.damage.DamageType;
|
||||||
import net.minestom.server.event.*;
|
import net.minestom.server.event.*;
|
||||||
import net.minestom.server.instance.InstanceContainer;
|
import net.minestom.server.instance.InstanceContainer;
|
||||||
import net.minestom.server.inventory.Inventory;
|
import net.minestom.server.inventory.Inventory;
|
||||||
@ -97,7 +98,7 @@ public class PlayerInit {
|
|||||||
Entity entity = event.getTarget();
|
Entity entity = event.getTarget();
|
||||||
if (entity instanceof EntityCreature) {
|
if (entity instanceof EntityCreature) {
|
||||||
EntityCreature creature = (EntityCreature) entity;
|
EntityCreature creature = (EntityCreature) entity;
|
||||||
creature.damage(-1);
|
creature.damage(DamageType.fromPlayer(player), -1);
|
||||||
Vector velocity = player.getPosition().clone().getDirection().multiply(6);
|
Vector velocity = player.getPosition().clone().getDirection().multiply(6);
|
||||||
velocity.setY(4f);
|
velocity.setY(4f);
|
||||||
entity.setVelocity(velocity, 150);
|
entity.setVelocity(velocity, 150);
|
||||||
@ -107,7 +108,7 @@ public class PlayerInit {
|
|||||||
Vector velocity = player.getPosition().clone().getDirection().multiply(4);
|
Vector velocity = player.getPosition().clone().getDirection().multiply(4);
|
||||||
velocity.setY(3.5f);
|
velocity.setY(3.5f);
|
||||||
target.setVelocity(velocity, 150);
|
target.setVelocity(velocity, 150);
|
||||||
target.damage(5);
|
target.damage(DamageType.fromPlayer(player), 5);
|
||||||
player.sendMessage("ATTACK");
|
player.sendMessage("ATTACK");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
73
src/main/java/fr/themode/demo/commands/GamemodeCommand.java
Normal file
73
src/main/java/fr/themode/demo/commands/GamemodeCommand.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package fr.themode.demo.commands;
|
||||||
|
|
||||||
|
import fr.themode.command.Arguments;
|
||||||
|
import fr.themode.command.Command;
|
||||||
|
import fr.themode.command.arguments.Argument;
|
||||||
|
import fr.themode.command.arguments.ArgumentType;
|
||||||
|
import net.minestom.server.entity.GameMode;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command that make a player change gamemode
|
||||||
|
*/
|
||||||
|
public class GamemodeCommand extends Command<Player> {
|
||||||
|
public GamemodeCommand() {
|
||||||
|
super("gamemode", "g", "gm");
|
||||||
|
|
||||||
|
setCondition(this::isAllowed);
|
||||||
|
|
||||||
|
setDefaultExecutor(this::usage);
|
||||||
|
|
||||||
|
Argument player = ArgumentType.String("player");
|
||||||
|
|
||||||
|
GameMode[] gameModes = GameMode.values();
|
||||||
|
String[] names = new String[gameModes.length];
|
||||||
|
for (int i = 0; i < gameModes.length; i++) {
|
||||||
|
names[i] = gameModes[i].name().toLowerCase();
|
||||||
|
}
|
||||||
|
Argument mode = ArgumentType.Word("mode").from(names);
|
||||||
|
|
||||||
|
addCallback(this::gameModeCallback, mode);
|
||||||
|
|
||||||
|
addSyntax(this::executeOnSelf, mode);
|
||||||
|
addSyntax(this::executeOnOther, player, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void usage(Player player, Arguments arguments) {
|
||||||
|
player.sendMessage("Usage: /gamemode [player] <gamemode>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeOnSelf(Player player, Arguments arguments) {
|
||||||
|
String gamemodeName = arguments.getWord("mode");
|
||||||
|
GameMode mode = GameMode.valueOf(gamemodeName.toUpperCase());
|
||||||
|
assert mode != null; // mode is not supposed to be null, because gamemodeName will be valid
|
||||||
|
player.setGameMode(mode);
|
||||||
|
player.sendMessage("You are now playing in "+gamemodeName);
|
||||||
|
|
||||||
|
System.out.println("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeOnOther(Player player, Arguments arguments) {
|
||||||
|
String gamemodeName = arguments.getWord("mode");
|
||||||
|
String targetName = arguments.getString("player");
|
||||||
|
GameMode mode = GameMode.valueOf(gamemodeName.toUpperCase());
|
||||||
|
assert mode != null; // mode is not supposed to be null, because gamemodeName will be valid
|
||||||
|
Optional<Player> target = player.getInstance().getPlayers().stream().filter(p -> p.getUsername().equals(targetName)).findFirst();
|
||||||
|
if(target.isPresent()) {
|
||||||
|
target.get().setGameMode(mode);
|
||||||
|
target.get().sendMessage("You are now playing in "+gamemodeName);
|
||||||
|
} else {
|
||||||
|
player.sendMessage("'"+targetName+"' is not a valid player name.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gameModeCallback(Player player, String gamemode, int error) {
|
||||||
|
player.sendMessage("'"+gamemode+"' is not a valid gamemode!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAllowed(Player player) {
|
||||||
|
return true; // TODO: permissions
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ public class HealthCommand extends Command<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean condition(Player player) {
|
private boolean condition(Player player) {
|
||||||
// Your custom condition, called no matter the syntax used
|
// TODO: Your custom condition, called no matter the syntax used
|
||||||
boolean hasPerm = true;
|
boolean hasPerm = true;
|
||||||
if (!hasPerm) {
|
if (!hasPerm) {
|
||||||
player.sendMessage("You do not have permission !");
|
player.sendMessage("You do not have permission !");
|
||||||
|
@ -245,6 +245,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||||||
if (!isOnGround()) {
|
if (!isOnGround()) {
|
||||||
float strength = gravityDragPerTick * gravityTickCounter;
|
float strength = gravityDragPerTick * gravityTickCounter;
|
||||||
|
|
||||||
|
boolean foundBlock = false;
|
||||||
int firstBlock = 0;
|
int firstBlock = 0;
|
||||||
for (int y = (int) position.getY(); y > 0; y--) {
|
for (int y = (int) position.getY(); y > 0; y--) {
|
||||||
BlockPosition blockPosition = new BlockPosition(position.getX(), y, position.getZ());
|
BlockPosition blockPosition = new BlockPosition(position.getX(), y, position.getZ());
|
||||||
@ -253,6 +254,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||||||
// System.out.println("id: " + blockId);
|
// System.out.println("id: " + blockId);
|
||||||
if (blockId != 0) {
|
if (blockId != 0) {
|
||||||
firstBlock = y;
|
firstBlock = y;
|
||||||
|
foundBlock = true;
|
||||||
//System.out.println("first block: " + y);
|
//System.out.println("first block: " + y);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -260,7 +262,10 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||||||
|
|
||||||
float newY = position.getY() - strength;
|
float newY = position.getY() - strength;
|
||||||
//System.out.println("REFRESH Y " + newY);
|
//System.out.println("REFRESH Y " + newY);
|
||||||
newY = Math.max(newY, firstBlock);
|
// allow entities to go below the world
|
||||||
|
if(foundBlock) {
|
||||||
|
newY = Math.max(newY, firstBlock);
|
||||||
|
}
|
||||||
refreshPosition(position.getX(), newY, position.getZ());
|
refreshPosition(position.getX(), newY, position.getZ());
|
||||||
gravityTickCounter++;
|
gravityTickCounter++;
|
||||||
if (isOnGround()) { // Round Y axis when gravity movement is done
|
if (isOnGround()) { // Round Y axis when gravity movement is done
|
||||||
@ -276,6 +281,8 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleVoid();
|
||||||
|
|
||||||
// Call the abstract update method
|
// Call the abstract update method
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@ -291,6 +298,16 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How does this entity handle being in the void?
|
||||||
|
*/
|
||||||
|
protected void handleVoid() {
|
||||||
|
// Kill if in void
|
||||||
|
if(getInstance().isInVoid(this.position)) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
||||||
this.eventCallbacks.put(eventClass, callback);
|
this.eventCallbacks.put(eventClass, callback);
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ package net.minestom.server.entity;
|
|||||||
|
|
||||||
public enum GameMode {
|
public enum GameMode {
|
||||||
|
|
||||||
SURVIVAL((byte) 0), CREATIVE((byte) 1), ADVENTURE((byte) 2), SPECTATOR((byte) 3);
|
SURVIVAL((byte) 0, true), CREATIVE((byte) 1, false), ADVENTURE((byte) 2, true), SPECTATOR((byte) 3, false);
|
||||||
|
|
||||||
private byte id;
|
private byte id;
|
||||||
private boolean hardcore;
|
private boolean hardcore;
|
||||||
|
private boolean canTakeDamage;
|
||||||
|
|
||||||
GameMode(byte id) {
|
GameMode(byte id, boolean canTakeDamage) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.canTakeDamage = canTakeDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHardcore(boolean hardcore) {
|
public void setHardcore(boolean hardcore) {
|
||||||
@ -22,4 +24,8 @@ public enum GameMode {
|
|||||||
public boolean isHardcore() {
|
public boolean isHardcore() {
|
||||||
return hardcore;
|
return hardcore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canTakeDamage() {
|
||||||
|
return canTakeDamage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.entity;
|
package net.minestom.server.entity;
|
||||||
|
|
||||||
import net.minestom.server.collision.BoundingBox;
|
import net.minestom.server.collision.BoundingBox;
|
||||||
|
import net.minestom.server.entity.damage.DamageType;
|
||||||
import net.minestom.server.entity.property.Attribute;
|
import net.minestom.server.entity.property.Attribute;
|
||||||
import net.minestom.server.event.DeathEvent;
|
import net.minestom.server.event.DeathEvent;
|
||||||
import net.minestom.server.event.PickupItemEvent;
|
import net.minestom.server.event.PickupItemEvent;
|
||||||
@ -103,7 +104,10 @@ public abstract class LivingEntity extends Entity {
|
|||||||
callEvent(DeathEvent.class, deathEvent);
|
callEvent(DeathEvent.class, deathEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void damage(float value) {
|
public void damage(DamageType type, float value) {
|
||||||
|
if(isImmune(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
EntityAnimationPacket entityAnimationPacket = new EntityAnimationPacket();
|
EntityAnimationPacket entityAnimationPacket = new EntityAnimationPacket();
|
||||||
entityAnimationPacket.entityId = getEntityId();
|
entityAnimationPacket.entityId = getEntityId();
|
||||||
entityAnimationPacket.animation = EntityAnimationPacket.Animation.TAKE_DAMAGE;
|
entityAnimationPacket.animation = EntityAnimationPacket.Animation.TAKE_DAMAGE;
|
||||||
@ -111,6 +115,15 @@ public abstract class LivingEntity extends Entity {
|
|||||||
setHealth(getHealth() - value);
|
setHealth(getHealth() - value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this entity immune to the given type of damage?
|
||||||
|
* @param type the type of damage
|
||||||
|
* @return true iff this entity is immune to the given type of damage
|
||||||
|
*/
|
||||||
|
public boolean isImmune(DamageType type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public float getHealth() {
|
public float getHealth() {
|
||||||
return health;
|
return health;
|
||||||
}
|
}
|
||||||
@ -195,4 +208,12 @@ public abstract class LivingEntity extends Entity {
|
|||||||
setAttribute(attribute, attribute.getDefaultValue());
|
setAttribute(attribute, attribute.getDefaultValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleVoid() {
|
||||||
|
// Kill if in void
|
||||||
|
if(getInstance().isInVoid(this.position)) {
|
||||||
|
damage(DamageType.VOID, 10f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package net.minestom.server.entity;
|
package net.minestom.server.entity;
|
||||||
|
|
||||||
|
import club.thectm.minecraft.text.TextBuilder;
|
||||||
import club.thectm.minecraft.text.TextObject;
|
import club.thectm.minecraft.text.TextObject;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.bossbar.BossBar;
|
import net.minestom.server.bossbar.BossBar;
|
||||||
import net.minestom.server.chat.Chat;
|
import net.minestom.server.chat.Chat;
|
||||||
import net.minestom.server.collision.BoundingBox;
|
import net.minestom.server.collision.BoundingBox;
|
||||||
|
import net.minestom.server.entity.damage.DamageType;
|
||||||
import net.minestom.server.entity.property.Attribute;
|
import net.minestom.server.entity.property.Attribute;
|
||||||
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
|
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
|
||||||
import net.minestom.server.event.*;
|
import net.minestom.server.event.*;
|
||||||
@ -75,6 +77,11 @@ public class Player extends LivingEntity {
|
|||||||
private Team team;
|
private Team team;
|
||||||
private BelowNameScoreboard belowNameScoreboard;
|
private BelowNameScoreboard belowNameScoreboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last damage source to hit this player, used to display the death message.
|
||||||
|
*/
|
||||||
|
private DamageType lastDamageSource;
|
||||||
|
|
||||||
// Abilities
|
// Abilities
|
||||||
private boolean invulnerable;
|
private boolean invulnerable;
|
||||||
private boolean flying;
|
private boolean flying;
|
||||||
@ -108,6 +115,14 @@ public class Player extends LivingEntity {
|
|||||||
setCanPickupItem(true); // By default
|
setCanPickupItem(true); // By default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void damage(DamageType type, float value) {
|
||||||
|
if(!isImmune(type)) {
|
||||||
|
lastDamageSource = type;
|
||||||
|
}
|
||||||
|
super.damage(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
|
|
||||||
@ -228,6 +243,33 @@ public class Player extends LivingEntity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kill() {
|
||||||
|
if(!isDead()) {
|
||||||
|
// send death message to player
|
||||||
|
TextObject deathMessage;
|
||||||
|
if(lastDamageSource != null) {
|
||||||
|
deathMessage = lastDamageSource.buildDeathMessage();
|
||||||
|
} else { // may happen if killed by the server without applying damage
|
||||||
|
deathMessage = TextBuilder.of("Killed by poor programming.").build();
|
||||||
|
}
|
||||||
|
CombatEventPacket deathPacket = CombatEventPacket.death(this, Optional.empty(), deathMessage);
|
||||||
|
playerConnection.sendPacket(deathPacket);
|
||||||
|
|
||||||
|
// send death message to chat
|
||||||
|
TextObject chatMessage;
|
||||||
|
if(lastDamageSource != null) {
|
||||||
|
chatMessage = lastDamageSource.buildChatMessage(this);
|
||||||
|
} else { // may happen if killed by the server without applying damage
|
||||||
|
chatMessage = TextBuilder.of(getUsername()+" was killed by poor programming.").build();
|
||||||
|
}
|
||||||
|
MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(player -> {
|
||||||
|
player.sendMessage(chatMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super.kill();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawn() {
|
public void spawn() {
|
||||||
|
|
||||||
@ -412,11 +454,11 @@ public class Player extends LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void damage(float value) {
|
public boolean isImmune(DamageType type) {
|
||||||
if (getGameMode() == GameMode.CREATIVE)
|
if(getGameMode().canTakeDamage()) {
|
||||||
return;
|
return type != DamageType.VOID;
|
||||||
|
}
|
||||||
super.damage(value);
|
return super.isImmune(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -456,6 +498,11 @@ public class Player extends LivingEntity {
|
|||||||
return !itemDropEvent.isCancelled();
|
return !itemDropEvent.isCancelled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Position getRespawnPoint() {
|
||||||
|
// TODO: Custom
|
||||||
|
return new Position(0f, 70f, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
public void respawn() {
|
public void respawn() {
|
||||||
if (!isDead())
|
if (!isDead())
|
||||||
return;
|
return;
|
||||||
@ -466,7 +513,7 @@ public class Player extends LivingEntity {
|
|||||||
respawnPacket.gameMode = getGameMode();
|
respawnPacket.gameMode = getGameMode();
|
||||||
respawnPacket.levelType = getLevelType();
|
respawnPacket.levelType = getLevelType();
|
||||||
getPlayerConnection().sendPacket(respawnPacket);
|
getPlayerConnection().sendPacket(respawnPacket);
|
||||||
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(getPosition());
|
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(getRespawnPoint());
|
||||||
callEvent(PlayerRespawnEvent.class, respawnEvent);
|
callEvent(PlayerRespawnEvent.class, respawnEvent);
|
||||||
refreshIsDead(false);
|
refreshIsDead(false);
|
||||||
|
|
||||||
@ -610,6 +657,14 @@ public class Player extends LivingEntity {
|
|||||||
return gameMode;
|
return gameMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff this player is in creative. Used for code readability
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isCreative() {
|
||||||
|
return gameMode == GameMode.CREATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
// Require sending chunk data after
|
// Require sending chunk data after
|
||||||
public void sendDimension(Dimension dimension) {
|
public void sendDimension(Dimension dimension) {
|
||||||
if (dimension == null)
|
if (dimension == null)
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.minestom.server.entity.damage;
|
||||||
|
|
||||||
|
import club.thectm.minecraft.text.TextBuilder;
|
||||||
|
import club.thectm.minecraft.text.TextObject;
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a type of damage
|
||||||
|
*/
|
||||||
|
public class DamageType {
|
||||||
|
|
||||||
|
public static final DamageType VOID = new DamageType("void");
|
||||||
|
public static final DamageType GRAVITY = new DamageType("gravity");
|
||||||
|
private final String identifier;
|
||||||
|
|
||||||
|
public DamageType(String identifier) {
|
||||||
|
this.identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextObject buildChatMessage(Player killed) {
|
||||||
|
return TextBuilder.of(killed.getUsername()+" was killed by damage of type "+identifier).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextObject buildDeathMessage() {
|
||||||
|
return TextBuilder.of("Killed by damage of type "+identifier).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DamageType fromPlayer(Player player) {
|
||||||
|
return new EntityDamage(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DamageType fromProjectile(Entity shooter, Entity projectile) {
|
||||||
|
return new EntityProjectileDamage(shooter, projectile);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package net.minestom.server.entity.damage;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents damage inflicted by an entity
|
||||||
|
*/
|
||||||
|
public class EntityDamage extends DamageType {
|
||||||
|
|
||||||
|
private final Entity source;
|
||||||
|
|
||||||
|
public EntityDamage(Entity source) {
|
||||||
|
super("entity_source");
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package net.minestom.server.entity.damage;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents damage inflicted by an entity, via a projectile
|
||||||
|
*/
|
||||||
|
public class EntityProjectileDamage extends DamageType {
|
||||||
|
|
||||||
|
private Entity shooter;
|
||||||
|
private final Entity projectile;
|
||||||
|
|
||||||
|
public EntityProjectileDamage(Entity shooter, Entity projectile) {
|
||||||
|
super("projectile_source");
|
||||||
|
this.shooter = shooter;
|
||||||
|
this.projectile = projectile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getProjectile() {
|
||||||
|
return projectile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getShooter() {
|
||||||
|
return shooter;
|
||||||
|
}
|
||||||
|
}
|
@ -140,17 +140,29 @@ public class Chunk implements Viewable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public short getBlockId(int x, int y, int z) {
|
public short getBlockId(int x, int y, int z) {
|
||||||
short id = blocksId[getBlockIndex(x, y, z)];
|
int index = getBlockIndex(x, y, z);
|
||||||
|
if(index < 0 || index >= blocksId.length) {
|
||||||
|
return 0; // TODO: custom invalid block
|
||||||
|
}
|
||||||
|
short id = blocksId[index];
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getCustomBlockId(int x, int y, int z) {
|
public short getCustomBlockId(int x, int y, int z) {
|
||||||
short id = customBlocksId[getBlockIndex(x, y, z)];
|
int index = getBlockIndex(x, y, z);
|
||||||
|
if(index < 0 || index >= blocksId.length) {
|
||||||
|
return 0; // TODO: custom invalid block
|
||||||
|
}
|
||||||
|
short id = customBlocksId[index];
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomBlock getCustomBlock(int x, int y, int z) {
|
public CustomBlock getCustomBlock(int x, int y, int z) {
|
||||||
short id = customBlocksId[getBlockIndex(x, y, z)];
|
int index = getBlockIndex(x, y, z);
|
||||||
|
if(index < 0 || index >= blocksId.length) {
|
||||||
|
return null; // TODO: custom invalid block
|
||||||
|
}
|
||||||
|
short id = customBlocksId[index];
|
||||||
return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null;
|
return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +173,9 @@ public class Chunk implements Viewable {
|
|||||||
|
|
||||||
protected void refreshBlockValue(int x, int y, int z, short blockId, short customId) {
|
protected void refreshBlockValue(int x, int y, int z, short blockId, short customId) {
|
||||||
int blockIndex = getBlockIndex(x, y, z);
|
int blockIndex = getBlockIndex(x, y, z);
|
||||||
|
if(blockIndex < 0 || blockIndex >= blocksId.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.blocksId[blockIndex] = blockId;
|
this.blocksId[blockIndex] = blockId;
|
||||||
this.customBlocksId[blockIndex] = customId;
|
this.customBlocksId[blockIndex] = customId;
|
||||||
|
@ -92,6 +92,14 @@ public abstract class Instance implements BlockModifier, DataContainer {
|
|||||||
|
|
||||||
public abstract boolean hasEnabledAutoChunkLoad();
|
public abstract boolean hasEnabledAutoChunkLoad();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a position in the void. If true, entities should take damage and die.
|
||||||
|
* Always returning false allow entities to survive in the void
|
||||||
|
* @param position the position in the world
|
||||||
|
* @return true iif position is inside the void
|
||||||
|
*/
|
||||||
|
public abstract boolean isInVoid(Position position);
|
||||||
|
|
||||||
//
|
//
|
||||||
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
|
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
|
||||||
ByteBuf chunkData = chunk.getFullDataPacket();
|
ByteBuf chunkData = chunk.getFullDataPacket();
|
||||||
|
@ -16,6 +16,7 @@ import net.minestom.server.particle.Particle;
|
|||||||
import net.minestom.server.particle.ParticleCreator;
|
import net.minestom.server.particle.ParticleCreator;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
import net.minestom.server.utils.ChunkUtils;
|
import net.minestom.server.utils.ChunkUtils;
|
||||||
|
import net.minestom.server.utils.Position;
|
||||||
import net.minestom.server.utils.SerializerUtils;
|
import net.minestom.server.utils.SerializerUtils;
|
||||||
import net.minestom.server.world.Dimension;
|
import net.minestom.server.world.Dimension;
|
||||||
|
|
||||||
@ -331,6 +332,12 @@ public class InstanceContainer extends Instance {
|
|||||||
return autoChunkLoad;
|
return autoChunkLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInVoid(Position position) {
|
||||||
|
// TODO: customizable
|
||||||
|
return position.getY() < -64;
|
||||||
|
}
|
||||||
|
|
||||||
protected void addSharedInstance(SharedInstance sharedInstance) {
|
protected void addSharedInstance(SharedInstance sharedInstance) {
|
||||||
this.sharedInstances.add(sharedInstance);
|
this.sharedInstances.add(sharedInstance);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.minestom.server.entity.Player;
|
|||||||
import net.minestom.server.instance.batch.BlockBatch;
|
import net.minestom.server.instance.batch.BlockBatch;
|
||||||
import net.minestom.server.instance.batch.ChunkBatch;
|
import net.minestom.server.instance.batch.ChunkBatch;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
|
import net.minestom.server.utils.Position;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -133,6 +134,11 @@ public class SharedInstance extends Instance {
|
|||||||
return instanceContainer.hasEnabledAutoChunkLoad();
|
return instanceContainer.hasEnabledAutoChunkLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInVoid(Position position) {
|
||||||
|
return instanceContainer.isInVoid(position);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBlock(int x, int y, int z, short blockId, Data data) {
|
public void setBlock(int x, int y, int z, short blockId, Data data) {
|
||||||
instanceContainer.setBlock(x, y, z, blockId, data);
|
instanceContainer.setBlock(x, y, z, blockId, data);
|
||||||
|
@ -53,7 +53,7 @@ public class ServerPacketIdentifier {
|
|||||||
public static final int OPEN_SIGN_EDITOR = 0x30;
|
public static final int OPEN_SIGN_EDITOR = 0x30;
|
||||||
public static final int CRAFT_RECIPE_RESPONSE = 0x31;
|
public static final int CRAFT_RECIPE_RESPONSE = 0x31;
|
||||||
public static final int PLAYER_ABILITIES = 0x32;
|
public static final int PLAYER_ABILITIES = 0x32;
|
||||||
public static final int COMBAT_EVENT = 0x33; // Do not seem to be used by the client
|
public static final int COMBAT_EVENT = 0x33;
|
||||||
public static final int PLAYER_INFO = 0x34;
|
public static final int PLAYER_INFO = 0x34;
|
||||||
public static final int FACE_PLAYER = 0x35;
|
public static final int FACE_PLAYER = 0x35;
|
||||||
public static final int PLAYER_POSITION_AND_LOOK = 0x36;
|
public static final int PLAYER_POSITION_AND_LOOK = 0x36;
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package net.minestom.server.network.packet.server.play;
|
||||||
|
|
||||||
|
import club.thectm.minecraft.text.TextObject;
|
||||||
|
import net.minestom.server.chat.Chat;
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.network.packet.PacketWriter;
|
||||||
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet sent during combat to a player.
|
||||||
|
* Only death is supported for the moment (other events are ignored anyway as of 1.15.2)
|
||||||
|
*/
|
||||||
|
public class CombatEventPacket implements ServerPacket {
|
||||||
|
|
||||||
|
private EventType type;
|
||||||
|
private int duration;
|
||||||
|
private int opponent;
|
||||||
|
private int playerID;
|
||||||
|
private TextObject deathMessage;
|
||||||
|
|
||||||
|
private CombatEventPacket() {}
|
||||||
|
|
||||||
|
public static CombatEventPacket enter() {
|
||||||
|
CombatEventPacket packet = new CombatEventPacket();
|
||||||
|
packet.type = EventType.ENTER_COMBAT;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CombatEventPacket end(int durationInTicks, Optional<Entity> opponent) {
|
||||||
|
CombatEventPacket packet = new CombatEventPacket();
|
||||||
|
packet.type = EventType.END_COMBAT;
|
||||||
|
packet.duration = durationInTicks;
|
||||||
|
packet.opponent = opponent.map(Entity::getEntityId).orElse(-1);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CombatEventPacket death(Player player, Optional<Entity> killer, TextObject message) {
|
||||||
|
CombatEventPacket packet = new CombatEventPacket();
|
||||||
|
packet.type = EventType.DEATH;
|
||||||
|
packet.playerID = player.getEntityId();
|
||||||
|
packet.opponent = killer.map(Entity::getEntityId).orElse(-1);
|
||||||
|
packet.deathMessage = message;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(PacketWriter writer) {
|
||||||
|
writer.writeVarInt(type.ordinal());
|
||||||
|
switch (type) {
|
||||||
|
case ENTER_COMBAT:
|
||||||
|
// nothing to add
|
||||||
|
break;
|
||||||
|
|
||||||
|
case END_COMBAT:
|
||||||
|
writer.writeVarInt(duration);
|
||||||
|
writer.writeInt(opponent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DEATH:
|
||||||
|
writer.writeVarInt(playerID);
|
||||||
|
writer.writeInt(opponent);
|
||||||
|
writer.writeSizedString(deathMessage.toJson().toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return ServerPacketIdentifier.COMBAT_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EventType {
|
||||||
|
ENTER_COMBAT, END_COMBAT, // both ignored by Notchian client
|
||||||
|
DEATH,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user