Git Product home page Git Product logo

minestompvp's Introduction

MinestomPvP

license platform

MinestomPvP is an extension for Minestom. It tries to mimic vanilla (modern and pre-1.9) PvP as good as possible, while also focusing on customizability and usability.

But, MinestomPvP does not only provide PvP, it also provides everything around it (e.g., status effects and food). You can easily pick which features you want to use.

The maven repository is available on jitpack.

MinestomPvP has been rewritten, see Important changes

Table of Contents

Important changes

MinestomPvP has recently been rewritten. Most features are now independent of each other and can be used separately.

Major changes include:

  • PvpExtension is now MinestomPvP
  • Configs have been removed in favor of a feature based configuration system
  • Registries are no longer prefixed by Custom but by Combat
  • PvpPlayer is now CombatPlayer, CustomPlayer is now CombatPlayerImpl

Features

Currently, most vanilla PvP features are supported.

  • Attack cooldown
  • Damage invulnerability
  • Weapons
  • Armor
  • Shields (or sword blocking)
  • Food
  • Totems
  • Bows and crossbows
  • Tridents (with riptide or loyalty)
  • Fishing rods (only hooking entities or legacy knockback, not fishing)
  • Other projectiles (potions, snowballs, eggs, ender pearls)
  • All enchantments possible with the above features (this includes protection, sharpness, knockback, ...)
  • Fall damage
  • End crystals
  • TNT
  • Respawn anchors (explosion only)

Plans

  • Lingering potions
  • Fireworks (for crossbows)
  • Support for (some) water mechanics (e.g. slowing projectiles down)
  • 1.21 features (the library is already 1.21 compatible, just doesn't support its combat features)
  • Rework of the tool & armor registry to allow for customization

Usage

Before doing anything else, you should call MinestomPvP.init(). This will make sure everything is registered correctly.

After you've initialized the library, you can start using combat features. For the most basic setup you can use the following:

MinestomPvP.init();

CombatFeatureSet modernVanilla = CombatFeatures.modernVanilla();
MinecraftServer.getGlobalEventHandler().addChild(modernVanilla.createNode());

This will give you a full vanilla experience without any customization.

Every combat feature has a createNode() method, which returns an EventNode with all listeners of the feature attached. This event node can be added to another event node to enable the feature within that scope. In the example above, it is being added to the global event handler, which means the feature will work everywhere.

The combat feature used in this example is a CombatFeatureSet. This is essentially a container for a list of combat features. There are two feature sets already defined by MinestomPvP:

  • Full modern combat, CombatFeatures.modernVanilla()
  • Full legacy (pre-1.9) combat, CombatFeatures.legacyVanilla()

Customization

The CombatFeatures class contains a field for every individual combat feature which has been defined by MinestomPvP itself. For example, you can add fall damage to your instance like so:

Instance instance;

CombatFeatureSet featureSet = CombatFeatures.empty()
        .version(CombatVersion.MODERN)
        .add(CombatFeatures.VANILLA_FALL)
        .add(CombatFeatures.VANILLA_PLAYER_STATE)
        .build();
instance.eventNode().addChild(featureSet.createNode());

As you can see, CombatFeatures.empty() provides you with a builder-like structure (CombatConfiguration) to which features can be added.

This combat configuration also contains convenience methods:

  • version(CombatVersion) to set a combat version, which is used by some vanilla features to adjust values which are different across versions
  • difficulty(DifficultyProvider) to set a difficulty provider, which is used by some vanilla features containing behavior which is different depending on the difficulty

In the example above, a PLAYER_STATE feature is added alongside the FALL feature, because the fall feature depends on it. CombatConfiguration takes care of handling these dependencies for you. The order in which the features are added does not matter. It is also possible to leave out the PLAYER_STATE feature: a NO_OP feature will then be used, which in this case will always signal to the fall feature that the player is not climbing.

Upon calling CombatConfiguration#build(), the combat configuration resolves all these dependencies and creates a CombatFeatureSet in which all the features are instantiated.

Features defined inside the CombatFeatures class are not yet instantiated, but are a DefinedFeature. The CombatConfiguration will instantiate the features for you, which will turn them into CombatFeature instances. An instantiated feature always knows its dependencies.

Legacy PvP

Earlier minecraft versions (pre-1.9) used a different PvP system, which to this day is still preferred by some. Legacy is the term used to describe this type of PvP throughout the library. You can get the CombatFeatureSet for legacy PvP using CombatFeatures.legacyVanilla().

To disable attack cooldown for a player, use MinestomPvP.setLegacyAttack(player, true). To enable the cooldown again, use false instead of true.

Knockback

A lot of servers like to customize their 1.8 knockback. It is also possible to do so with this extension. In EntityKnockbackEvent, you can set a LegacyKnockbackSettings object. It contains information about how the knockback is calculated. A builder is obtainable by using LegacyKnockbackSettings.builder(). For more information, check the config of BukkitOldCombatMechanics.

Integration

To integrate this extension into your minestom server, you may have to tweak a little bit to make sure everything works correctly.

The extension uses a custom player implementation, if you use one, it is recommended to extend CombatPlayerImpl. If you for some reason can't, make sure to implement CombatPlayer in a similar fashion to CombatPlayerImpl. The implementation of MinestomPvP is registered inside MinestomPvP.init(), so register yours after initializing the library.

To allow explosions, you have to register an explosion supplier to every instance in which they are used. Implementations of ExplosionFeature might provide an explosion supplier.

CombatFeatureSet featureSet;
Instance instance;

instance.setExplosionSupplier(featureSet.get(FeatureType.EXPLOSION).getExplosionSupplier());

Keep in mind that the explosion supplier can be different depending on the explosion feature, so always register the one from the explosion feature which is active in the instance.

Registries

MinestomPvP has several registries, which you can also register to in order to create custom behavior:

  • CombatEnchantments: a registry of enchantment behaviors, used by EnchantmentFeature
  • CombatPotionEffects: a registry of potion effect behaviors, used by EffectFeature
  • CombatPotionTypes: a registry of potion types and which effects they contain, used by EffectFeature

You can use the static #register(...) method in those classes to add custom entries.

You can also use the class Tool, which contains all tools and their properties (not all properties are currently included, will change soon). The same applies to ToolMaterial (wood, stone, ...) and ArmorMaterial.

Events

The library provides several events:

  • DamageBlockEvent: cancellable, called when an entity blocks damage using a shield. This event can be used to set the remaining damage.
  • EntityKnockbackEvent: cancellable, called when an entity gets knocked back by another entity. Gets called twice for weapons with the knockback enchantment (once for default damage knockback, once for the extra knockback). This event can be used to set the knockback strength.
  • EntityPreDeathEvent: cancellable, a form of EntityDeathEvent but cancellable and with a damage type. Can be used to cancel the death while still applying after-damage effects, such as attack sounds.
  • EquipmentDamageEvent: cancellable, called when an item in an equipment slot gets damaged.
  • ExplosionEvent: cancellable, called when an explosion will take place. Can be used to modify the affected blocks.
  • FinalAttackEvent: cancellable, called when a player attacks an entity. Can be used to set a few variables like sprint, critical, sweeping, etc.
  • FinalDamageEvent: cancellable, called when the final damage calculation (including armor and effects) is completed. This event should be used instead of EntityDamageEvent, unless you want to detect how much damage was originally dealt.
  • FishingBobberRetrieveEvent: cancellable, called when a player retrieves a fishing bobber.
  • LegacyKnockbackEvent: cancellable, called when an entity gets knocked back by another entity using legacy pvp. Same applies as for EntityKnockbackEvent. This event can be used to change the knockback settings.
  • PickupEntityEvent: cancellable, called when a player picks up an entity (arrow or trident).
  • PlayerExhaustEvent: cancellable, called when a players' exhaustion level changes.
  • PlayerRegenerateEvent: cancellable, called when a player naturally regenerates health.
  • PlayerSpectateEvent: cancellable, called when a spectator tries to spectate an entity by attacking it.
  • PotionVisibilityEvent: cancellable, called when an entities potion state (ambient, particle color and invisibility) is updated.
  • TotemUseEvent: cancellable, called when a totem prevents an entity from dying.

Custom combat features

It is possible to create your own combat features, which can extend an existing one or be completely independent. Below is an explanation followed by an example.

In order to be compatible with the library, your combat features must implement CombatFeature. It is also possible to implement RegistrableFeature instead, which will provide you with a createNode() method. In this case, you must also implement RegistrableFeature#init(EventNode), which attaches all the listeners to the given event node.

After this, you must create a FeatureType for your custom feature. If you are implementing an existing feature, use existing feature types in the FeatureType class. Otherwise, you can create your own using FeatureType.of(String, F). The first argument will be the name, the second the NO_OP feature which will be used when no implementation is present. It is recommended to create an interface for your custom feature type which extends CombatFeature (or RegistrableFeature). This way, you can easily specify methods to expose to other features.

Lastly, it is needed to create a DefinedFeature instance for your custom implementation. This defined feature defines an implementation of your feature type, and it can be used to add your implementation to a combat configuration.

Example of a custom feature type, with 1 method which can be used by other features:

interface MyCustomFeature extends CombatFeature {
	MyCustomFeature NO_OP = new MyCustomFeature() {};
	
	FeatureType TYPE = FeatureType.of("MY_CUSTOM", NO_OP);
	
	boolean isItWorking();
}

Example of an implementation of this custom feature type, which listens for events and implements the method:

class MyCustomFeatureImpl implements MyCustomFeature, RegistrableFeature {
    public static final DefinedFeature<MyCustomFeatureImpl> DEFINED = new DefinedFeature<>(
            MyCustomFeature.TYPE, configuration -> new MyCustomFeatureImpl()
    );

    @Override
    public void init(EventNode<PlayerInstanceEvent> node) {
        node.addListener(PlayerChatEvent.class, event -> {
            // Do something...
        });
    }

    @Override
    public boolean isItWorking() {
        return true;
    }
}

Now you can use your own feature:

MinecraftServer.getGlobalEventHandler().addChild(
        CombatFeatures.single(MyCustomFeatureImpl.DEFINED)
);

As you can see, it is also possible to use CombatFeatures.single(DefinedFeature) to instantiate a single feature without dependencies.

Depending on other features

Say, you want to access a players fall distance in your own feature. You can do this by depending on FallFeature.

class MyCustomFeatureImpl implements MyCustomFeature {
    public static final DefinedFeature<MyCustomFeatureImpl> DEFINED = new DefinedFeature<>(
            MyCustomFeature.TYPE, configuration -> new MyCustomFeatureImpl(configuration),
            FeatureType.FALL
    );

    private final FeatureConfiguration configuration;
    private FallFeature fallFeature;

    public MyCustomFeatureImpl(FeatureConfiguration configuration) {
        this.configuration = configuration;
    }

    @Override
    public void initDependencies() {
        this.fallFeature = configuration.get(FeatureType.FALL);
    }

    @Override
    public boolean isItWorking() {
        Player player; // Some player
        return fallFeature.getFallDistance(player) > 3;
    }
}

Note that the FeatureConfiguration which can be used to get the FallFeature from is only ready when initDependencies() is called. This is due to complications with recursive dependencies between features.

Player init

You might want to initialize certain things for a player upon joining or whenever they get reset (respawn). This is possible by using the player init of a defined feature. Its constructor can also take a DefinedFeature.PlayerInit. This is a class whose init(Player player, boolean firstInit) method will be called upon a player join or reset.

You can for example use this player init to set tags on a player. The vanilla implementation of FallFeature uses it to set the fall distance tag on the player to 0.

There are two criteria to use the player init:

  • The logic does not depend on other features and as such can be defined once for every feature implementation, and not for every instance of this implementation.
  • The logic is required to be ran for the feature to work. If this is done inside the feature itself it might be out of scope (e.g. player join event is not called on an instance, so the feature might miss it).

Contributing

You can contribute in multiple ways. If you have an issue or a great idea, you can open an issue. You may also open a new pull request if you have made something for this project and you think it will fit in well.

If anything does not integrate with your project, you can also open an issue (or submit a pull request). I aim towards making this extension as usable as possible!

Credits

Thanks to kiip1 for testing and finding bugs.

I used BukkitOldCombatMechanics as a resource for recreating legacy pvp.

minestompvp's People

Contributors

am-gone avatar codestech1 avatar demkom58 avatar dhruviyengar avatar hsgamer avatar kiip1 avatar themode avatar togar2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

minestompvp's Issues

Snowballs can get stuck inside blocks

When player crouches and shoots snowball at pitch more than 60, snowball will get stuck in block without removing itself.
Upon removing block, snowball starts falling again and can interact with blocks normally.

[BUG] Materal.isFood() method does not exist

Hello! I am running minestorm 1c528d8ae2 and minestormpvp a373305 and I have a issue regarding an unknown method. I have initialized minestormpvp, registered default events, and when I start the server it throws an exception:

Exception in thread "main" java.lang.NoSuchMethodError: 'boolean net.minestom.server.item.Material.isFood()'
	at io.github.togar2.pvp.food.FoodComponents.loadFromRegistry(FoodComponents.java:141)
	at io.github.togar2.pvp.food.FoodComponents.registerAll(FoodComponents.java:126)
	at io.github.togar2.pvp.PvpExtension.init(PvpExtension.java:52)

The material class does not have the method isFood()

Knockback in water no less

Players get exactly the same knockback in water as if the water is not there. In Vanilla the player gets less knockback in the water

Question: Adding PVP-Events only to specific instance

Hey, I'm trying to get get PVP on an specific instance working.

Minestom's eventNode.appendChild within an Instance accepts as an Parameter
EventNode<? extends InstanceEvent>

MinestomPvp returs from PvpExtension.legacyEvents() or the new Builder EventNode <EntityEvent>

In all examples are always global event listener used, is there a way to register pvp events on specific Instances or even only players?

Thanks!

ProjectileCollideWithBlockEvent not called when projectiles implemented wih minestompvp

I have a listener for ProjectileCollideWithBlockEvent, however it is not called when I throw a snowball (even though I have it implemented with this code:)

node.addChild(
			PvPConfig
				.emptyBuilder()
				.projectile(ProjectileConfig.emptyBuilder(false).snowball(true).build())
				.build().createNode()
		)

I need to use this event as opposed to the ProjectileBlockHitEvent, because that class does not contain things I need like collissionPosition and block.

FinallAttack Bug?

Hello, I have the following problem...
If I want to hit a player it only works 2-3 times then it doesn't work anymore. It only works if the player I want to hit gets damaged again by something else (e.g. falling damage) In my opinion this is not normal xD

New 1.21 features

1.21 added several features and fixes related to combat

  • Mace
  • Mace enchantments
  • Wind breeze
  • New wind charged potion effect
  • Fire aspect doesn't work when blocked with a shield
  • Sweeping now applies fire aspect to all mobs
  • Sweeping now applies thorns to all mobs
  • Sweeping now applies the right type of damage when combined with mob type specific enchantments (e.g. smite)
  • Burn time of fire protection now stacks when multiple armor pieces are equipped
  • Knockback reduction of blast protection now stacks in the same way
  • Burning time now also has an attribute
  • Use safe fall distance attribute (increases with jump boost)
  • Use entity hit range attribute

And most importantly

  • Support for new 1.21 enchantment effects specified in json files (there really is no other way in order to keep up with future updates..)

Checking which block snowball hits

There is no way to get position of hit block from ProjectileBlockHitEvent.
For projectiles with hitanticipation, there is no way to tell hit block location, because it is calculated as pos.add(velocity*0.06), but at time of event firing, velocity is already set to zero.

EntityDamageEvent is always cancelled

Line 134 of the DamageListener.java class will consistently cancel the DamageEvent.

    @Override
    public void setCancelled(boolean cancel) {
        StackFrame frame = StackWalker.getInstance().walk(stream -> stream.skip(1).findFirst().get());
        System.out.println("DamageEvent Cancellation changed by "
                + frame.getFileName() + ":" + frame.getLineNumber() + " to "
                + cancel);
        this.cancelled = cancel;
    }

During testing, I compiled a custom Minestom version that changes the setCancelled function of the EntityDamageEvent to the above code- effectively printing out whenever the value was changed, and by what.

In my own code, I listened to the EntityDamageEvent with a priority of -1 to run this;

System.out.println("Damage event " + event.getDamage() + " Cancelled? " + event.isCancelled()
					+ " Tracker Invuln " + Tracker.invulnerableTime.get(event.getEntity().getUuid())
					+ " Last Dmg " + Tracker.lastDamageTaken.getOrDefault(event.getEntity().getUuid(), -1f) + "");

This gives the following output; (using a Diamond Sword against another player)

Damage event 1.61024 Cancelled? false Tracker Invuln 20 Last Dmg 10.525438

DamageEvent Cancellation changed by DamageListener.java:134 to true

PvP code is started as so, and MinestomPvP is used as an extension, compiled from Jitpack's Maven Repo. Happy to attach anything else that is needed/debug further.

GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
EventNode<EntityEvent> pvpEvents = PvpExtension.events();
globalEventHandler.addChild(pvpEvents);

Feel free to DM on Discord- Gatt#0010

Elytra support

Support for flying with elytra (e.g. taking less fall damage, crashing into walls)

Error

java.lang.NoSuchMethodError: 'void net.minestom.server.network.packet.server.play.SoundEffectPacket.(net.minestom.server.sound.SoundEvent, net.kyori.adventure.sound.Sound$Source, net.minestom.server.coordinate.Point, float, float)'
at Ext_PvpExtension//io.github.bloepiloepi.pvp.listeners.DamageListener.handleEntityDamage(DamageListener.java:377)
at Ext_PvpExtension//io.github.bloepiloepi.pvp.listeners.DamageListener.lambda$events$0(DamageListener.java:56)
at net.minestom.server.event.EventListener$Builder$1.run(EventListener.java:151)
at net.minestom.server.event.EventNodeImpl$Handle.callListener(EventNodeImpl.java:488)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$listenersConsumer$8(EventNodeImpl.java:420)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:391)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.call(EventNodeImpl.java:324)
at net.minestom.server.event.EventNode.call(EventNode.java:190)
at net.minestom.server.event.EventNode.callCancellable(EventNode.java:215)
at net.minestom.server.event.EventDispatcher.callCancellable(EventDispatcher.java:18)
at net.minestom.server.entity.LivingEntity.damage(LivingEntity.java:340)
at Ext_PvpExtension//io.github.bloepiloepi.pvp.listeners.FallDamageHandler.handleFallDamage(FallDamageHandler.java:81)
at Ext_PvpExtension//io.github.bloepiloepi.pvp.listeners.DamageListener.lambda$events$2(DamageListener.java:72)
at net.minestom.server.event.EventListener$Builder$1.run(EventListener.java:151)
at net.minestom.server.event.EventNodeImpl$Handle.callListener(EventNodeImpl.java:488)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$listenersConsumer$8(EventNodeImpl.java:420)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:391)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.lambda$createConsumer$5(EventNodeImpl.java:399)
at net.minestom.server.event.EventNodeImpl$Handle.call(EventNodeImpl.java:324)
at net.minestom.server.event.EventNode.call(EventNode.java:190)
at net.minestom.server.event.EventDispatcher.call(EventDispatcher.java:10)
at net.minestom.server.listener.PlayerPositionListener.processMovement(PlayerPositionListener.java:57)
at net.minestom.server.listener.PlayerPositionListener.playerPositionListener(PlayerPositionListener.java:24)
at net.minestom.server.listener.manager.PacketListenerManager.processClientPacket(PacketListenerManager.java:90)
at net.minestom.server.entity.Player.lambda$interpretPacketQueue$16(Player.java:1808)
at org.jctools.queues.MpscUnboundedXaddArrayQueue.drain(MpscUnboundedXaddArrayQueue.java:312)
at net.minestom.server.entity.Player.interpretPacketQueue(Player.java:1808)
at net.minestom.server.entity.Player.update(Player.java:345)
at net.minestom.server.entity.Entity.tick(Entity.java:541)
at net.minestom.server.thread.TickThread.tick(TickThread.java:66)
at net.minestom.server.thread.TickThread.run(TickThread.java:41)

Maven Repo

I'm looking into using the MinestomPvP API in my server but can't seem to find a Maven/Gradle repository to do so. What's the recommended way to hook into the API?

No support for the latest Minestom version

Hey, I tried to use this on my Minestom CE server and it sadly failed to work. The Extension class isn't present in the latest Minestom versions anymore. Can you look at that?

Arrow not shooting from bow and crossbow

When trying to shoot an arrow from a bow or crossbow, no arrow shoots or gets used.

This happens when registering either CombatFeatures.modernVanilla() or CombatFeatures.VANILLA_PROJECTILE_ITEM and CombatFeatures.VANILLA_BOW (or crossbow).

Events for Explosion Damage on Blocks & Players

  • Some events that give a list of affected blocks so that developers can remove some of the blocks (like protection blocks).
  • The knockback will not be applied to the player if the player's damage event is cancelled.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.