/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.network.entity.vanilla;
import static org.lanternpowered.server.network.entity.EntityProtocolManager.INVALID_ENTITY_ID;
import com.flowpowered.math.vector.Vector3d;
import org.lanternpowered.server.data.key.LanternKeys;
import org.lanternpowered.server.entity.event.EntityEvent;
import org.lanternpowered.server.entity.event.RefreshAbilitiesPlayerEvent;
import org.lanternpowered.server.entity.living.player.LanternPlayer;
import org.lanternpowered.server.entity.living.player.gamemode.LanternGameMode;
import org.lanternpowered.server.inventory.LanternItemStack;
import org.lanternpowered.server.network.buffer.ByteBuffer;
import org.lanternpowered.server.network.buffer.ByteBufferAllocator;
import org.lanternpowered.server.network.entity.EntityProtocolInitContext;
import org.lanternpowered.server.network.entity.EntityProtocolUpdateContext;
import org.lanternpowered.server.network.entity.parameter.ByteBufParameterList;
import org.lanternpowered.server.network.entity.parameter.ParameterList;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutDestroyEntities;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutEntityMetadata;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutPlayerAbilities;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSetGameMode;
import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayOutSpawnObject;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
import org.spongepowered.api.entity.living.player.gamemode.GameModes;
import org.spongepowered.api.item.ItemTypes;
import java.util.UUID;
public class PlayerEntityProtocol extends HumanoidEntityProtocol<LanternPlayer> {
private boolean lastHasNoGravity;
private GameMode lastGameMode = GameModes.NOT_SET;
private int elytraRocketId = INVALID_ENTITY_ID;
private boolean lastElytraFlying;
private boolean lastElytraSpeedBoost;
private boolean lastCanFly;
private float lastFlySpeed;
public PlayerEntityProtocol(LanternPlayer entity) {
super(entity);
setTickRate(1);
}
@Override
protected void init(EntityProtocolInitContext context) {
super.init(context);
this.elytraRocketId = context.acquire();
}
@Override
protected void remove(EntityProtocolInitContext context) {
super.remove(context);
context.release(this.elytraRocketId);
this.elytraRocketId = INVALID_ENTITY_ID;
}
@Override
protected void spawn(EntityProtocolUpdateContext context) {
super.spawn(context);
context.sendToSelf(() -> new MessagePlayOutEntityMetadata(getRootEntityId(), fillParameters(true)));
final GameMode gameMode = getEntity().get(Keys.GAME_MODE).get();
context.sendToSelf(() -> new MessagePlayOutSetGameMode((LanternGameMode) gameMode));
context.sendToSelf(() -> new MessagePlayOutPlayerAbilities(
this.entity.get(Keys.IS_FLYING).orElse(false), canFly(), false, gameMode == GameModes.CREATIVE,
this.entity.get(Keys.FLYING_SPEED).orElse(0.0).floatValue(), 0.01f));
}
@Override
protected void update(EntityProtocolUpdateContext context) {
final GameMode gameMode = this.entity.get(Keys.GAME_MODE).get();
final boolean canFly = canFly();
final float flySpeed = this.entity.get(Keys.FLYING_SPEED).orElse(0.0).floatValue();
if (gameMode != this.lastGameMode) {
context.sendToSelf(() -> new MessagePlayOutSetGameMode((LanternGameMode) gameMode));
context.sendToSelf(() -> new MessagePlayOutPlayerAbilities(
this.entity.get(Keys.IS_FLYING).orElse(false), canFly, false, gameMode == GameModes.CREATIVE, flySpeed, 0.01f));
this.lastGameMode = gameMode;
this.lastCanFly = canFly;
this.lastFlySpeed = flySpeed;
} else if (canFly != this.lastCanFly || flySpeed != this.lastFlySpeed) {
context.sendToSelf(() -> new MessagePlayOutPlayerAbilities(
this.entity.get(Keys.IS_FLYING).orElse(false), canFly, false, gameMode == GameModes.CREATIVE, flySpeed, 0.01f));
this.lastCanFly = canFly;
this.lastFlySpeed = flySpeed;
}
super.update(context);
// Some 1.11.2 magic, ultra secret stuff...
final boolean elytraFlying = this.entity.get(LanternKeys.IS_ELYTRA_FLYING).orElse(false);
final boolean elytraSpeedBoost = this.entity.get(LanternKeys.ELYTRA_SPEED_BOOST).orElse(false);
if (this.lastElytraFlying != elytraFlying || this.lastElytraSpeedBoost != elytraSpeedBoost) {
if (this.lastElytraFlying && this.lastElytraSpeedBoost) {
context.sendToAll(() -> new MessagePlayOutDestroyEntities(this.elytraRocketId));
} else if (elytraFlying && elytraSpeedBoost) {
// Create the fireworks data item
final LanternItemStack itemStack = new LanternItemStack(ItemTypes.FIREWORKS);
// Write the item to a parameter list
final ByteBufParameterList parameterList = new ByteBufParameterList(ByteBufferAllocator.unpooled());
parameterList.add(EntityParameters.Fireworks.ITEM, itemStack);
parameterList.add(EntityParameters.Fireworks.ELYTRA_BOOST_PLAYER, getRootEntityId());
parameterList.getByteBuffer().ifPresent(ByteBuffer::retain);
context.sendToAll(() -> new MessagePlayOutSpawnObject(this.elytraRocketId, UUID.randomUUID(), 76, 0,
this.entity.getPosition(), 0, 0, Vector3d.ZERO));
context.sendToAll(() -> new MessagePlayOutEntityMetadata(this.elytraRocketId, parameterList));
}
this.lastElytraSpeedBoost = elytraSpeedBoost;
this.lastElytraFlying = elytraFlying;
}
}
@Override
protected void handleEvent(EntityProtocolUpdateContext context, EntityEvent event) {
if (event instanceof RefreshAbilitiesPlayerEvent) {
final GameMode gameMode = this.entity.get(Keys.GAME_MODE).get();
final float flySpeed = this.entity.get(Keys.FLYING_SPEED).orElse(0.0).floatValue();
context.sendToSelf(() -> new MessagePlayOutPlayerAbilities(
this.entity.get(Keys.IS_FLYING).orElse(false), canFly(), false, gameMode == GameModes.CREATIVE, flySpeed, 0.01f));
} else {
super.handleEvent(context, event);
}
}
private boolean canFly() {
// TODO: Double jump?
return this.entity.get(Keys.CAN_FLY).orElse(false) ||
(this.entity.get(LanternKeys.SUPER_STEVE).orElse(false) && !this.entity.get(LanternKeys.IS_ELYTRA_FLYING).orElse(false));
}
@Override
protected void spawn(ParameterList parameterList) {
super.spawn(parameterList);
parameterList.add(EntityParameters.Humanoid.SCORE, this.entity.get(LanternKeys.SCORE).orElse(0));
parameterList.add(EntityParameters.Humanoid.ADDITIONAL_HEARTS, 0f);
}
@Override
protected void update(ParameterList parameterList) {
super.update(parameterList);
final boolean hasNoGravity = hasNoGravity();
if (hasNoGravity != this.lastHasNoGravity) {
parameterList.add(EntityParameters.Base.NO_GRAVITY, hasNoGravity);
this.lastHasNoGravity = hasNoGravity;
}
}
@Override
boolean hasNoGravity() {
return !this.entity.get(Keys.HAS_GRAVITY).orElse(true);
}
}