Add legacy CTF support

This commit is contained in:
Electroid 2017-07-09 15:22:10 -07:00 committed by Pablete1234
parent 25353865b4
commit 52c82a81e7
No known key found for this signature in database
GPG Key ID: DAFF9A337EF9A5FA
8 changed files with 265 additions and 18 deletions

View File

@ -2,10 +2,19 @@ package tc.oc.pgm.flag;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.pgm.map.inject.MapBinders;
import tc.oc.pgm.match.MatchPlayerFacetBinder;
import tc.oc.pgm.match.inject.MatchBinders;
public class FlagManifest extends HybridManifest implements MapBinders, MatchBinders {
public class FlagManifest extends HybridManifest implements MapBinders {
@Override
protected void configure() {
rootParsers().addBinding().to(FlagParser.class);
installPlayerModule(binder -> {
final MatchPlayerFacetBinder facets = new MatchPlayerFacetBinder(binder);
facets.register(LegacyFlagPlayerFacet.class);
});
}
}

View File

@ -0,0 +1,150 @@
package tc.oc.pgm.flag;
import com.google.common.collect.ImmutableList;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import tc.oc.commons.bukkit.item.ItemBuilder;
import tc.oc.commons.bukkit.util.NMSHacks;
import tc.oc.commons.core.stream.Collectors;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.flag.event.FlagStateChangeEvent;
import tc.oc.pgm.flag.state.Carried;
import tc.oc.pgm.flag.state.Spawned;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayerFacet;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.match.Repeatable;
import tc.oc.time.Time;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import static java.util.stream.IntStream.range;
import static tc.oc.minecraft.protocol.MinecraftVersion.*;
@ListenerScope(MatchScope.LOADED)
public class LegacyFlagPlayerFacet implements MatchPlayerFacet, Listener {
private final Match match;
private final Player bukkit;
private final Map<Flag, Beam> beams;
@Inject LegacyFlagPlayerFacet(Match match, Player bukkit) {
this.match = match;
this.bukkit = bukkit;
this.beams = new HashMap<>();
}
protected Stream<Flag> flags() {
return (Stream<Flag>) match.features().all(Flag.class);
}
protected void trackFlag(Flag flag) {
if(lessThan(MINECRAFT_1_8, bukkit.getProtocolVersion())) {
beams.put(flag, beams.getOrDefault(flag, new Beam(flag)));
}
}
protected void untrackFlag(Flag flag) {
if(beams.containsKey(flag)) {
beams.remove(flag).hide();
}
}
@Override
public void enable() {
flags().filter(flag -> flag.state() instanceof Spawned)
.forEach(this::trackFlag);
}
@Override
public void disable() {
flags().forEach(this::untrackFlag);
beams.clear();
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onFlagStateChange(FlagStateChangeEvent event) {
Flag flag = event.getFlag();
untrackFlag(flag);
if(event.getNewState() instanceof Spawned) {
trackFlag(flag);
}
}
@Repeatable(interval = @Time(seconds = 1))
public void onSecond() {
ImmutableList.copyOf(beams.values()).forEach(Beam::update);
}
class Beam {
final Flag flag;
final List<NMSHacks.FakeZombie> segments;
Beam(Flag flag) {
this.flag = flag;
this.segments = range(0, 32).mapToObj(i -> new NMSHacks.FakeZombie(match.getWorld(), true, true))
.collect(Collectors.toImmutableList());
show();
}
Optional<Player> carrier() {
return Optional.ofNullable(flag.state() instanceof Carried ? ((Carried) flag.state()).getCarrier().getBukkit() : null);
}
Location location() {
Location location = flag.getLocation().get().clone();
location.setPitch(0);
return location;
}
ItemStack wool() {
return new ItemBuilder().material(Material.WOOL)
.enchant(Enchantment.DURABILITY, 1)
.color(flag.getDyeColor())
.get();
}
void show() {
if(carrier().map(carrier -> carrier.equals(bukkit)).orElse(false)) return;
segments.forEach(segment -> {
segment.spawn(bukkit, location());
segment.wear(bukkit, EquipmentSlot.HEAD, wool());
});
range(1, segments.size()).forEachOrdered(i -> {
segments.get(i - 1).ride(bukkit, segments.get(i).entity());
});
update();
}
void update() {
Optional<Player> carrier = carrier();
NMSHacks.FakeZombie base = segments.get(0);
if(carrier.isPresent()) {
base.mount(bukkit, carrier.get());
} else {
base.teleport(bukkit, location());
}
}
void hide() {
for(int i = segments.size() - 1; i >= 0; i--) {
segments.get(i).destroy(bukkit);
}
}
}
}

View File

@ -61,6 +61,10 @@ public class Carried extends Spawned implements Missing {
this.dropLocations.add(dropLocation); // Need an initial dropLocation in case the carrier never generates ones
}
public MatchPlayer getCarrier() {
return carrier;
}
@Override
public boolean isRecoverable() {
return true;
@ -191,7 +195,7 @@ public class Carried extends Spawned implements Missing {
@Override
protected boolean canSeeParticles(Player player) {
return player != this.carrier.getBukkit();
return super.canSeeParticles(player) && player != this.carrier.getBukkit();
}
protected void dropFlag() {

View File

@ -5,6 +5,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tc.oc.commons.bukkit.util.NMSHacks;
import tc.oc.minecraft.protocol.MinecraftVersion;
import tc.oc.pgm.flag.Flag;
import tc.oc.pgm.flag.Post;
import tc.oc.pgm.flag.event.FlagCaptureEvent;
@ -67,7 +68,7 @@ public abstract class Spawned extends BaseState {
}
protected boolean canSeeParticles(Player player) {
return true;
return MinecraftVersion.atLeast(MinecraftVersion.MINECRAFT_1_8, player.getProtocolVersion());
}
@Override

View File

@ -203,7 +203,22 @@ public interface Match extends Audience, IMatchQuery, Filterable<IMatchQuery>, M
* @see #registerEvents
* @see #registerRepeatable
*/
void registerEventsAndRepeatables(Object thing);
default void registerEventsAndRepeatables(Object thing) {
registerRepeatable(thing);
if(thing instanceof Listener) registerEvents((Listener) thing);
}
/**
* Unregister {@link Repeatable} methods on the given object, and also
* unregister it for events if it is a {@link Listener}.
*
* @see #unregisterEvents
* @see #unregisterRepeatable
*/
default void unregisterEventsAndRepeatables(Object thing) {
unregisterRepeatable(thing);
if(thing instanceof Listener) unregisterEvents((Listener) thing);
}
/**
* Return the {@link MapModuleContext} that was used to load this match.

View File

@ -352,14 +352,6 @@ public class MatchImpl implements Match, ForwardingAudience {
runningScheduler.unregisterRepeatables(object);
}
@Override
public void registerEventsAndRepeatables(Object thing) {
registerRepeatable(thing);
if(thing instanceof Listener) {
registerEvents((Listener) thing);
}
}
// -----------------------------------
// ---- Modules/Features/Contexts ----

View File

@ -7,6 +7,7 @@ import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.bukkit.entity.Player;
import org.bukkit.inventory.PlayerInventory;
import tc.oc.minecraft.protocol.MinecraftVersion;
/**
* Binds various services provided through a {@link Player} (but does not bind {@link Player} itself)
@ -26,4 +27,8 @@ public class BukkitPlayerModule extends AbstractModule {
@Provides UUID uuid(Player player) {
return player.getUniqueId();
}
@Provides MinecraftVersion version(Player player) {
return MinecraftVersion.byProtocol(player.getProtocolVersion());
}
}

View File

@ -22,6 +22,7 @@ import net.minecraft.server.DataWatcher;
import net.minecraft.server.Enchantment;
import net.minecraft.server.EntityArmorStand;
import net.minecraft.server.EntityChicken;
import net.minecraft.server.EntityFallingBlock;
import net.minecraft.server.EntityFireworks;
import net.minecraft.server.EntityItem;
import net.minecraft.server.EntityLiving;
@ -90,10 +91,10 @@ import org.bukkit.craftbukkit.util.Skins;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Egg;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.entity.Wither;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.material.MaterialData;
import org.bukkit.plugin.Plugin;
@ -102,6 +103,9 @@ import org.bukkit.potion.PotionEffectTypeWrapper;
import org.bukkit.registry.Key;
import org.bukkit.util.Vector;
import java.time.Duration;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import tc.oc.commons.core.util.TimeUtils;
import static com.google.common.base.Preconditions.checkArgument;
@ -114,7 +118,8 @@ public class NMSHacks {
// There is no nice way to get at them.
private static final Map<Class<? extends Entity>, Integer> ENTITY_TYPE_IDS = ImmutableMap.of(
org.bukkit.entity.Item.class, 2,
ArmorStand.class, 78
ArmorStand.class, 78,
FallingBlock.class, 70
);
private static EntityTrackerEntry getTrackerEntry(net.minecraft.server.Entity nms) {
@ -272,8 +277,12 @@ public class NMSHacks {
metadata);
}
public static Packet setPassengerPacket(int vehicleId, IntStream riderIds) {
return new PacketPlayOutMount(vehicleId, riderIds.toArray());
}
public static Packet setPassengerPacket(int vehicleId, int riderId) {
return new PacketPlayOutMount(vehicleId, riderId);
return setPassengerPacket(vehicleId, IntStream.of(riderId));
}
public static Packet moveEntityRelativePacket(int entityId, Vector delta, boolean onGround) {
@ -292,6 +301,10 @@ public class NMSHacks {
true);
}
public static Packet entityVelocityPacket(int entityId, Vector velocity) {
return new PacketPlayOutEntityVelocity(entityId, velocity.getX(), velocity.getY(), velocity.getZ());
}
private static Packet entityMetadataPacket(int entityId, net.minecraft.server.Entity nmsEntity, boolean complete) {
return new PacketPlayOutEntityMetadata(entityId, nmsEntity.getDataWatcher(), complete);
}
@ -304,8 +317,12 @@ public class NMSHacks {
return entityMetadataPacket(nmsEntity.getId(), nmsEntity, complete);
}
public static Packet entityEquipmentPacket(int entityId, EquipmentSlot slot, org.bukkit.inventory.ItemStack armor) {
return new PacketPlayOutEntityEquipment(entityId, EnumItemSlot.values()[slot.ordinal()], CraftItemStack.asNMSCopy(armor));
}
public static Packet entityHelmetPacket(int entityId, org.bukkit.inventory.ItemStack helmet) {
return new PacketPlayOutEntityEquipment(entityId, EnumItemSlot.HEAD, CraftItemStack.asNMSCopy(helmet));
return entityEquipmentPacket(entityId, EquipmentSlot.HEAD, helmet);
}
/**
@ -318,6 +335,8 @@ public class NMSHacks {
public interface FakeEntity {
int entityId();
Entity entity();
void spawn(Player viewer, Location location, Vector velocity);
default void spawn(Player viewer, Location location) {
@ -336,8 +355,20 @@ public class NMSHacks {
sendPacket(viewer, teleportEntityPacket(entityId(), location));
}
default void ride(Player viewer, Stream<Entity> riders) {
sendPacket(viewer, setPassengerPacket(entityId(), riders.mapToInt(Entity::getEntityId)));
}
default void ride(Player viewer, Entity rider) {
sendPacket(viewer, setPassengerPacket(entityId(), rider.getEntityId()));
ride(viewer, Stream.of(rider));
}
default void mount(Player viewer, Entity vehicle) {
sendPacket(viewer, setPassengerPacket(vehicle.getEntityId(), entityId()));
}
default void wear(Player viewer, EquipmentSlot slot, org.bukkit.inventory.ItemStack item) {
sendPacket(viewer, entityEquipmentPacket(entityId(), slot, item));
}
}
@ -348,6 +379,11 @@ public class NMSHacks {
this.entity = entity;
}
@Override
public Entity entity() {
return entity.getBukkitEntity();
}
@Override
public int entityId() {
return entity.getId();
@ -385,6 +421,26 @@ public class NMSHacks {
}
}
public static class FakeFallingBlock extends FakeEntityImpl<EntityFallingBlock> {
private final MaterialData data;
public FakeFallingBlock(World world, @Nullable MaterialData data) {
super(new EntityFallingBlock(((CraftWorld) world).getHandle()));
this.data = data != null ? data : new MaterialData(Material.SAND);
entity.setNoGravity(true);
entity.formBlock = false;
entity.dropItem = false;
entity.ticksLived = 1;
}
@Override
public void spawn(Player viewer, Location location, Vector velocity) {
sendPacket(viewer, spawnEntityPacket(FallingBlock.class, data.getItemTypeId() + (data.getData() << 12), entityId(), entity.getUniqueID(), location, velocity));
sendPacket(viewer, entityMetadataPacket(entity, true));
}
}
private static class FakeLivingEntity<T extends EntityLiving> extends FakeEntityImpl<T> {
protected FakeLivingEntity(T entity) {
@ -440,12 +496,22 @@ public class NMSHacks {
}
public static class FakeZombie extends FakeLivingEntity<EntityZombie> {
public FakeZombie(World world, boolean invisible) {
this(world, invisible, false);
}
public FakeZombie(World world, boolean invisible, boolean baby) {
super(new EntityZombie(((CraftWorld) world).getHandle()));
entity.setInvisible(invisible);
entity.setNoAI(false);
entity.setBaby(baby);
entity.setNoAI(true);
entity.setNoGravity(true);
entity.setInvulnerable(true);
entity.setSilent(true);
}
}
public static class FakeChicken extends FakeLivingEntity<EntityChicken> {
@ -494,6 +560,11 @@ public class NMSHacks {
this.uuid = uuid;
}
@Override
public Entity entity() {
return prototype().getBukkitEntity();
}
@Override
public int entityId() {
return entityId;