Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
7cdff81b36 | |||
a0a4619755 | |||
7acdda21fa | |||
e1f6bfcf0b | |||
7d69e1db4a |
21 changed files with 639 additions and 607 deletions
7
pom.xml
7
pom.xml
|
@ -17,8 +17,13 @@
|
|||
<dependency>
|
||||
<groupId>net.minestom</groupId>
|
||||
<artifactId>minestom-snapshots</artifactId>
|
||||
<version>d606051f1e</version>
|
||||
<version>7dd1792096</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.0.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,6 +1,7 @@
|
|||
package net.pivipi;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.GameMode;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
|
@ -9,25 +10,24 @@ import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
|||
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
||||
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.Weather;
|
||||
import net.pivipi.ball.Ball;
|
||||
import net.pivipi.ball.BallKicker;
|
||||
import net.pivipi.physics.aabb.AABBPlayer;
|
||||
import net.pivipi.world.Stadium;
|
||||
import net.pivipi.game.Game;
|
||||
import net.pivipi.game.Stadium;
|
||||
|
||||
public class LoginHandler {
|
||||
private final Instance spawningInstance;
|
||||
private final Stadium stadium = new Stadium();
|
||||
private final Game game;
|
||||
|
||||
public LoginHandler(Instance spawningInstance) {
|
||||
this.spawningInstance = spawningInstance;
|
||||
this.game = new Game(new Stadium(spawningInstance, new Vec(-50, 64, -50), new Vec(100)));
|
||||
}
|
||||
|
||||
public void setup(GlobalEventHandler globalEventHandler) {
|
||||
globalEventHandler.addListener(AsyncPlayerConfigurationEvent.class, event -> onLogin(event));
|
||||
globalEventHandler.addListener(PlayerDisconnectEvent.class, event -> onQuit(event));
|
||||
globalEventHandler.addListener(PlayerSpawnEvent.class, event -> onSpawn(event));
|
||||
new BallKicker(stadium).setup(globalEventHandler);
|
||||
new BallKicker(game.stadium).setup(globalEventHandler);
|
||||
}
|
||||
|
||||
private void onLogin(AsyncPlayerConfigurationEvent event) {
|
||||
|
@ -37,11 +37,11 @@ public class LoginHandler {
|
|||
event.setHardcore(true);
|
||||
|
||||
player.setRespawnPoint(new Pos(0, 67, 0));
|
||||
stadium.players.add((AABBPlayer)player);
|
||||
game.addPlayer(player);
|
||||
}
|
||||
|
||||
private void onQuit(PlayerDisconnectEvent event) {
|
||||
stadium.players.remove(event.getPlayer());
|
||||
game.removePlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
private void onSpawn(PlayerSpawnEvent event) {
|
||||
|
@ -51,11 +51,6 @@ public class LoginHandler {
|
|||
player.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.2);
|
||||
player.getAttribute(Attribute.GENERIC_JUMP_STRENGTH).setBaseValue(0.36813); // just enough to jump a block
|
||||
|
||||
if (stadium.ball == null) {
|
||||
Ball ball = new Ball(stadium);
|
||||
ball.setInstance(event.getInstance());
|
||||
ball.teleport(new Pos(0, 90, 5));
|
||||
stadium.ball = ball;
|
||||
}
|
||||
game.startWarmup();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,14 @@
|
|||
package net.pivipi;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.GlobalEventHandler;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
import net.minestom.server.instance.LightingChunk;
|
||||
import net.minestom.server.instance.Weather;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.PlayerProvider;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.registry.DynamicRegistry.Key;
|
||||
import net.minestom.server.timer.SchedulerManager;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
import net.pivipi.physics.aabb.AABBPlayer;
|
||||
import net.pivipi.world.FancyDimension;
|
||||
import net.pivipi.world.SoccerGenerator;
|
||||
import net.pivipi.world.WorldConstraints;
|
||||
|
@ -29,9 +18,10 @@ public class Main {
|
|||
System.setProperty("minestom.tps", "100");
|
||||
|
||||
MinecraftServer minecraftServer = MinecraftServer.init();
|
||||
ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
||||
|
||||
InstanceManager instanceManager = MinecraftServer.getInstanceManager();
|
||||
GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
|
||||
SchedulerManager schedulerManager = MinecraftServer.getSchedulerManager();
|
||||
|
||||
Key<DimensionType> dimension = FancyDimension.create();
|
||||
InstanceContainer instanceContainer = instanceManager.createInstanceContainer(dimension);
|
||||
|
@ -46,21 +36,15 @@ public class Main {
|
|||
WorldConstraints worldConstraints = new WorldConstraints();
|
||||
worldConstraints.setup(globalEventHandler);
|
||||
|
||||
connectionManager.setPlayerProvider(new PlayerProvider() {
|
||||
@Override
|
||||
public @NotNull Player createPlayer(@NotNull UUID uuid, @NotNull String username,
|
||||
@NotNull PlayerConnection connection) {
|
||||
return new AABBPlayer(uuid, username, connection);
|
||||
}
|
||||
});
|
||||
new Stats(instanceManager).start(schedulerManager);
|
||||
|
||||
/* done */
|
||||
|
||||
MinecraftServer.setCompressionThreshold(0);
|
||||
MinecraftServer.setBrandName("PiViPi");
|
||||
|
||||
minecraftServer.start("0.0.0.0", 25565);
|
||||
System.out.println("started");
|
||||
minecraftServer.start("127.0.0.1", 25565);
|
||||
System.out.println("Listening on port 25565");
|
||||
|
||||
}
|
||||
}
|
43
src/main/java/net/pivipi/Stats.java
Normal file
43
src/main/java/net/pivipi/Stats.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package net.pivipi;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
import net.minestom.server.timer.ExecutionType;
|
||||
import net.minestom.server.timer.SchedulerManager;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
|
||||
public class Stats implements Runnable {
|
||||
private int tickDelay = 10;
|
||||
private Runtime runtime = Runtime.getRuntime();
|
||||
private InstanceManager instanceManager;
|
||||
//private BenchmarkManager benchmarkManager;
|
||||
|
||||
private long lastRun;
|
||||
|
||||
public Stats(InstanceManager instanceManager) {
|
||||
this.instanceManager = instanceManager;
|
||||
}
|
||||
|
||||
public void start(SchedulerManager schedulerManager) {
|
||||
//this.benchmarkManager = MinecraftServer.getBenchmarkManager();
|
||||
schedulerManager.scheduleTask(this, TaskSchedule.immediate(), TaskSchedule.tick(tickDelay), ExecutionType.TICK_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long now = System.currentTimeMillis();
|
||||
long delay = now - lastRun;
|
||||
lastRun = now;
|
||||
|
||||
double mspt = (double)delay / tickDelay;
|
||||
long memUsed = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
|
||||
|
||||
Component footer = Component.text("%.3f MSPT | %dMB".formatted(mspt, memUsed));
|
||||
|
||||
instanceManager.getInstances().forEach(instance -> { // TODO make this async
|
||||
instance.getPlayers().forEach(player -> {
|
||||
player.sendPlayerListFooter(footer);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,19 +1,15 @@
|
|||
package net.pivipi.ball;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.metadata.other.FallingBlockMeta;
|
||||
import net.minestom.server.event.player.PlayerEntityInteractEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.pivipi.entity.PhysicsEntity;
|
||||
import net.pivipi.game.Stadium;
|
||||
import net.pivipi.physics.Physics;
|
||||
import net.pivipi.physics.aabb.AABBEntity;
|
||||
import net.pivipi.world.Stadium;
|
||||
|
||||
public class Ball extends AABBEntity {
|
||||
public class Ball extends PhysicsEntity {
|
||||
private long lastTick;
|
||||
private Player holder;
|
||||
|
||||
|
|
|
@ -1,27 +1,18 @@
|
|||
package net.pivipi.ball;
|
||||
|
||||
import javax.print.attribute.standard.MediaSize.Engineering;
|
||||
import javax.swing.plaf.basic.BasicInternalFrameTitlePane.IconifyAction;
|
||||
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.Entity.Pose;
|
||||
import net.minestom.server.event.GlobalEventHandler;
|
||||
import net.minestom.server.event.player.PlayerEntityInteractEvent;
|
||||
import net.minestom.server.event.player.PlayerHandAnimationEvent;
|
||||
import net.minestom.server.event.player.PlayerMoveEvent;
|
||||
import net.minestom.server.event.player.PlayerStartSneakingEvent;
|
||||
import net.minestom.server.event.player.PlayerStopSneakingEvent;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.BlockActionPacket;
|
||||
import net.minestom.server.particle.Particle.Item;
|
||||
import net.pivipi.physics.CollisionDetector;
|
||||
import net.pivipi.game.Stadium;
|
||||
import net.pivipi.physics.Collision;
|
||||
import net.pivipi.physics.CollisionData;
|
||||
import net.pivipi.world.Stadium;
|
||||
|
||||
public class BallKicker { // TODO apply physics settings here
|
||||
public final Stadium stadium;
|
||||
|
@ -50,7 +41,7 @@ public class BallKicker { // TODO apply physics settings here
|
|||
Player player = event.getPlayer();
|
||||
|
||||
Pos deltaPos = new Pos(0, 0.3, 0);
|
||||
boolean collides = CollisionDetector.collidesWithEntity(stadium.ball, player);
|
||||
boolean collides = Collision.collidesWithEntity(stadium.ball, player);
|
||||
if (collides) {
|
||||
System.out.println(deltaPos);
|
||||
stadium.ball.addPos(deltaPos.asVec());
|
||||
|
@ -86,8 +77,7 @@ public class BallKicker { // TODO apply physics settings here
|
|||
private void onSwing(PlayerHandAnimationEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
CollisionData collisionData = new CollisionData();
|
||||
CollisionDetector.pushOutside(player, stadium.ball, collisionData);
|
||||
CollisionData collisionData = Collision.pushOutside(player, stadium.ball, false);
|
||||
double distance = collisionData.distance.withY(0).distance(0, 0, 0);
|
||||
|
||||
if (collisionData.distance.y() < verticalReach && distance < reach) {
|
||||
|
|
54
src/main/java/net/pivipi/entity/AABB.java
Normal file
54
src/main/java/net/pivipi/entity/AABB.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package net.pivipi.entity;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
|
||||
public class AABB {
|
||||
Vec min, max;
|
||||
Vec velocity;
|
||||
|
||||
public AABB(Vec min, Vec max, Vec velocity) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the position of the lower corner
|
||||
* @return
|
||||
*/
|
||||
public Vec getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the position of the lower corner + velocity
|
||||
* @return
|
||||
*/
|
||||
public Vec getMinSwept() {
|
||||
return min.add(velocity);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the position of the upper corner
|
||||
* @return
|
||||
*/
|
||||
public Vec getMax() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the position of the upper corner + velocity
|
||||
* @return
|
||||
*/
|
||||
public Vec getMaxSwept() {
|
||||
return max.add(velocity);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the velocity that is blocks per second
|
||||
* @return
|
||||
*/
|
||||
public Vec getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
}
|
21
src/main/java/net/pivipi/entity/PhysicsEntity.java
Normal file
21
src/main/java/net/pivipi/entity/PhysicsEntity.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package net.pivipi.entity;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
|
||||
public class PhysicsEntity extends Entity {
|
||||
|
||||
public PhysicsEntity(@NotNull EntityType entityType) {
|
||||
super(entityType);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
public AABB getAabb() {
|
||||
Vec min = this.getPosition().sub(this.getBoundingBox().width() / 2, 0, this.getBoundingBox().depth() / 2).asVec();
|
||||
Vec max = this.getPosition().add(this.getBoundingBox().width() / 2, this.getBoundingBox().height(), this.getBoundingBox().depth() / 2).asVec();
|
||||
return new AABB(min, max, this.velocity);
|
||||
}
|
||||
}
|
104
src/main/java/net/pivipi/game/Game.java
Normal file
104
src/main/java/net/pivipi/game/Game.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
package net.pivipi.game;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.pivipi.ball.Ball;
|
||||
|
||||
public class Game {
|
||||
public final Stadium stadium;
|
||||
|
||||
public boolean started; // true even if warmup
|
||||
public boolean warmup;
|
||||
public boolean paused; // is game paused
|
||||
public long startedTime; // fine until year 292272993 but trust me it will pass in an Instant
|
||||
public long lostTime; // how much ms the game was paused for
|
||||
public long addedTime; // also called stoppage time, cosmetic since a person decides when to end the match
|
||||
|
||||
private long pauseStart;
|
||||
|
||||
public Game(Stadium stadium) {
|
||||
this.stadium = stadium;
|
||||
}
|
||||
|
||||
|
||||
public boolean addPlayer(Player player) {
|
||||
return stadium.players.add(player);
|
||||
}
|
||||
|
||||
public boolean removePlayer(Player player) {
|
||||
return stadium.players.remove(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* starts warmup
|
||||
* @return true if the game was not running
|
||||
*/
|
||||
public boolean startWarmup() {
|
||||
if (!started && !warmup) return false;
|
||||
|
||||
this.started = true;
|
||||
this.warmup = true;
|
||||
this.startedTime = System.currentTimeMillis();
|
||||
|
||||
Ball ball = new Ball(stadium);
|
||||
ball.setInstance(stadium.instance);
|
||||
ball.teleport(new Pos(stadium.max.sub(stadium.size.div(2))));
|
||||
stadium.ball = ball;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* starts the game
|
||||
* @return true if the game was not running or if there was warmup
|
||||
*/
|
||||
public boolean start() {
|
||||
if (started && !warmup) return false;
|
||||
|
||||
this.started = true;
|
||||
this.warmup = false;
|
||||
this.startedTime = System.currentTimeMillis();
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ends the game
|
||||
* @return true if the game was running
|
||||
*/
|
||||
public boolean end() {
|
||||
if (!started) return false;
|
||||
|
||||
this.started = false;
|
||||
this.warmup = false;
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean pause() {
|
||||
if (paused) return false;
|
||||
|
||||
pauseStart = System.currentTimeMillis();
|
||||
paused = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unpause() {
|
||||
if (!paused) return false;
|
||||
|
||||
lostTime += System.currentTimeMillis() - pauseStart;
|
||||
paused = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* suggests how much added time
|
||||
* basically rounds down lost time to minutes
|
||||
* @return
|
||||
*/
|
||||
public long suggestAddedTime() {
|
||||
return (lostTime / 60000) * 60000;
|
||||
}
|
||||
}
|
47
src/main/java/net/pivipi/game/PhysicsSettings.java
Normal file
47
src/main/java/net/pivipi/game/PhysicsSettings.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package net.pivipi.game;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class PhysicsSettings {
|
||||
public double gravity = 9.8;
|
||||
public double blockBounciness = 0.5;
|
||||
public double playerHeadBounciness = 0.5;
|
||||
public double playerSneakingHeadBounciness = 0.2;
|
||||
public double playerBodyBounciness = 0.2;
|
||||
public double playerSneakingBodyBounciness = 0;
|
||||
public double playerCarryStrength = 2;
|
||||
public double headJumpStrength = 5;
|
||||
public double headNodStep = 20;
|
||||
public double jumpStrength = 20;
|
||||
public double groundFriction = 0.5;
|
||||
public double airFriction = 0.1;
|
||||
|
||||
public String setByString(String key, String valueStr) {
|
||||
|
||||
try {
|
||||
Field field = this.getClass().getDeclaredField(key);
|
||||
Type type = field.getAnnotatedType().getType();
|
||||
Object value;
|
||||
|
||||
if (type.equals(double.class)) {
|
||||
value = Double.valueOf(valueStr);
|
||||
} else {
|
||||
return "This property is not changeable because I can't yet handle type " + type.getTypeName();
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(this, value);
|
||||
|
||||
return field.getName() + " is now " + value.toString();
|
||||
} catch (NumberFormatException e) {
|
||||
return "Not a decimal number: " + valueStr;
|
||||
} catch (NoSuchFieldException e) {
|
||||
return "Invalid key: " + key;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return "Invalid value: " + valueStr;
|
||||
} catch (IllegalAccessException e) {
|
||||
return "This property is not changeable";
|
||||
}
|
||||
}
|
||||
}
|
42
src/main/java/net/pivipi/game/Stadium.java
Normal file
42
src/main/java/net/pivipi/game/Stadium.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package net.pivipi.game;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.pivipi.ball.Ball;
|
||||
|
||||
public class Stadium {
|
||||
public final Set<Player> players = new HashSet<Player>();
|
||||
public Ball ball; // TODO public?
|
||||
|
||||
/**
|
||||
* the instance where this stadium is
|
||||
*/
|
||||
public final Instance instance;
|
||||
|
||||
/**
|
||||
* the lower corner pos
|
||||
*/
|
||||
public final Vec pos;
|
||||
|
||||
/**
|
||||
* the stadium size
|
||||
*/
|
||||
public final Vec size;
|
||||
|
||||
/**
|
||||
* the max corner pos that is pos + size
|
||||
*/
|
||||
public final Vec max;
|
||||
|
||||
|
||||
public Stadium(Instance instance, Vec pos, Vec size) {
|
||||
this.instance = instance;
|
||||
this.pos = pos;
|
||||
this.size = size;
|
||||
this.max = pos.add(size);
|
||||
}
|
||||
}
|
197
src/main/java/net/pivipi/physics/Collision.java
Normal file
197
src/main/java/net/pivipi/physics/Collision.java
Normal file
|
@ -0,0 +1,197 @@
|
|||
package net.pivipi.physics;
|
||||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.collision.SweepResult;
|
||||
import net.minestom.server.collision.VisibleSweepResult;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
|
||||
public class Collision {
|
||||
/**
|
||||
*
|
||||
* @param pos static object's pos
|
||||
* @param boundingBox static object's bounding box
|
||||
* @param moving moving entity
|
||||
* @param movement moving entity movement
|
||||
* @return
|
||||
*/
|
||||
private static CollisionData willCollide(Pos pos, BoundingBox boundingBox, Entity moving, Vec movement) {
|
||||
SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0);
|
||||
boolean collides = boundingBox.intersectBoxSwept(moving.getPosition(), movement, pos, moving.getBoundingBox(), sweepResult);
|
||||
VisibleSweepResult visibleSweepResult = new VisibleSweepResult(sweepResult);
|
||||
|
||||
CollisionData collisionData = new CollisionData();
|
||||
collisionData.swept = true;
|
||||
collisionData.percentage = visibleSweepResult.res;
|
||||
collisionData.collidedPosition = new Vec(visibleSweepResult.collidedPositionX, visibleSweepResult.collidedPositionY, visibleSweepResult.collidedPositionZ);
|
||||
collisionData.normal = new Vec(visibleSweepResult.normalX, visibleSweepResult.normalY, visibleSweepResult.normalZ);
|
||||
|
||||
return collides ? collisionData : null;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param entity the static entity
|
||||
* @param moving the moving entity
|
||||
* @param movement the movement
|
||||
* @return null if no collision
|
||||
*/
|
||||
public static CollisionData willCollideWithEntity(Entity entity, Entity moving, Vec movement) {
|
||||
return willCollide(entity.getPosition(), entity.getBoundingBox(), moving, movement);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groundY ground Y
|
||||
* @param moving the moving entity
|
||||
* @param movement the movement
|
||||
* @return null if no collision
|
||||
*/
|
||||
public static CollisionData willCollideWithGround(double groundY, Entity moving, Vec movement) {
|
||||
return willCollide(new Pos(Double.MIN_VALUE, 0, Double.MIN_VALUE), new BoundingBox(Double.MAX_VALUE, 64, Double.MAX_VALUE), moving, movement);
|
||||
}
|
||||
|
||||
public static CollisionData willCollideWithBlock(Pos blockPos, Entity moving, Vec movement) {
|
||||
return willCollide(blockPos, new BoundingBox(1, 1, 1), moving, movement);
|
||||
}
|
||||
|
||||
public static boolean collidesWithEntity(Entity entity1, Entity entity2) {
|
||||
return entity1.getBoundingBox().intersectEntity(entity1.getPosition(), entity2);
|
||||
}
|
||||
|
||||
public static boolean collidesWithGround(double groundY, Entity entity) {
|
||||
return new BoundingBox(10, 1, 10).intersectEntity(entity.getPosition().withY(groundY - 1).sub(5, 0, 5), entity);
|
||||
}
|
||||
|
||||
public static boolean collidesWithBlock(Pos blockPos, Entity entity) {
|
||||
return new BoundingBox(1, 1, 1).intersectEntity(blockPos, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pusher the entity that's pushing like a player
|
||||
* @param pushed the entity that's being pushed like a ball
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static CollisionData pushOutside(Entity pusher, Entity pushed, boolean y) {
|
||||
return pushOutside(pusher.getBoundingBox(), pusher.getPosition(), pushed, y);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pusher the entity that's pushing like a player
|
||||
* @param pushed the entity that's being pushed like a ball
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static CollisionData pushOutside(Pos blockPos, Entity pushed, boolean y) {
|
||||
return pushOutside(new BoundingBox(1, 1, 1), blockPos, pushed, y);
|
||||
}
|
||||
|
||||
|
||||
/*public static Vec pushOutside(BoundingBox pusherBox, Pos pusherPos, Entity pushed, CollisionData collisionData) {
|
||||
double ballXSize = pushed.getBoundingBox().width();
|
||||
double playerXSize = pusherBox.width();
|
||||
double ballZSize = pushed.getBoundingBox().depth();
|
||||
double playerZSize = pusherBox.depth();
|
||||
double ballHeight = pushed.getBoundingBox().height();
|
||||
double playerHeight = pusherBox.height();
|
||||
Pos ballCenterPos = pushed.getPosition();
|
||||
Pos playerCenterPos = pusherPos;
|
||||
|
||||
Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2); // corner
|
||||
Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2);
|
||||
Vec ballDistance = ballCenterPos.sub(playerCenterPos).asVec(); // center distance
|
||||
|
||||
if (collisionData != null)
|
||||
collisionData.distance = ballDistance;
|
||||
|
||||
if (ballDistance.y() > 0 && ballPos.y() > playerPos.y() + playerHeight) return null; // doesn't collide on y+
|
||||
else if (playerPos.y() > ballPos.y() + ballHeight) return null; // doesn't collide on y-
|
||||
|
||||
Vec newBallOffset = new Vec(0);
|
||||
double diffX = 0, diffZ = 0;
|
||||
|
||||
if (ballDistance.x() > 0) {
|
||||
diffX = ballPos.x() - (playerPos.x() + playerXSize);
|
||||
if (diffX < 0) // collides on x+
|
||||
newBallOffset = newBallOffset.withX(-diffX);
|
||||
} else {
|
||||
diffX = playerPos.x() - (ballPos.x() + ballXSize);
|
||||
if (diffX < 0) // collides on x-
|
||||
newBallOffset = newBallOffset.withX(diffX);
|
||||
}
|
||||
|
||||
if (ballDistance.z() > 0) {
|
||||
diffZ = ballPos.z() - (playerPos.z() + playerZSize);
|
||||
if (diffZ < 0) // collides on z+
|
||||
newBallOffset = newBallOffset.withZ(-diffZ);
|
||||
} else {
|
||||
diffZ = playerPos.z() - (ballPos.z() + ballZSize);
|
||||
if (diffZ < 0) // collides on z-
|
||||
newBallOffset = newBallOffset.withZ(diffZ);
|
||||
}
|
||||
|
||||
|
||||
if (diffX < 0 && diffZ < 0)
|
||||
return diffX < diffZ ? newBallOffset.withX(0) : newBallOffset.withZ(0);
|
||||
|
||||
return null;
|
||||
}*/
|
||||
/**
|
||||
*
|
||||
* @param pusherBox the entity that's pushing like a player or a block, its bounding box
|
||||
* @param pusherPos the position, bottom center
|
||||
* @param pushed the entity that's being pushed like a ball
|
||||
* @return collision data
|
||||
*/
|
||||
private static CollisionData pushOutside(BoundingBox pusherBox, Pos pusherPos, Entity pushed, boolean y) {
|
||||
double ballXSize = pushed.getBoundingBox().width();
|
||||
double playerXSize = pusherBox.width();
|
||||
double ballZSize = pushed.getBoundingBox().depth();
|
||||
double playerZSize = pusherBox.depth();
|
||||
double ballHeight = pushed.getBoundingBox().height();
|
||||
double playerHeight = pusherBox.height();
|
||||
|
||||
Pos ballCenterPos = pushed.getPosition();
|
||||
Pos playerCenterPos = pusherPos;
|
||||
|
||||
Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2);
|
||||
Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2);
|
||||
Vec ballDistance = ballCenterPos.sub(playerCenterPos).asVec();
|
||||
|
||||
CollisionData collisionData = new CollisionData();
|
||||
collisionData.distance = ballDistance;
|
||||
|
||||
double diffX = ballDistance.x() > 0
|
||||
? ballPos.x() - (playerPos.x() + playerXSize)
|
||||
: playerPos.x() - (ballPos.x() + ballXSize);
|
||||
double diffY = ballDistance.y() > 0
|
||||
? ballPos.y() - (playerPos.y() + playerHeight)
|
||||
: playerPos.y() - (ballPos.y() + ballHeight);
|
||||
double diffZ = ballDistance.z() > 0
|
||||
? ballPos.z() - (playerPos.z() + playerZSize)
|
||||
: playerPos.z() - (ballPos.z() + ballZSize);
|
||||
|
||||
Vec newBallOffset = new Vec(0);
|
||||
Vec offset = null;
|
||||
|
||||
if (diffX < 0 && diffZ < 0 && diffY < 0) {
|
||||
newBallOffset = newBallOffset.withX(ballDistance.x() > 0 ? -diffX : diffX);
|
||||
newBallOffset = newBallOffset.withY(ballDistance.x() > 0 ? -diffY : diffY);
|
||||
newBallOffset = newBallOffset.withZ(ballDistance.z() > 0 ? -diffZ : diffZ);
|
||||
|
||||
if (!y) diffY = Double.POSITIVE_INFINITY;
|
||||
|
||||
if (diffX < diffZ && diffX < diffY)
|
||||
offset = newBallOffset.mul(0, 0, 1);
|
||||
else if (diffY < diffX && diffY < diffZ)
|
||||
offset = newBallOffset.mul(0, 1, 0);
|
||||
else if (diffZ < diffX && diffZ < diffY)
|
||||
offset = newBallOffset.mul(1, 0, 0);
|
||||
}
|
||||
|
||||
collisionData.offset = offset;
|
||||
return collisionData;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +1,40 @@
|
|||
package net.pivipi.physics;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
|
||||
public class CollisionData {
|
||||
public boolean swept;
|
||||
|
||||
/**
|
||||
* distance between centers of entities (but bottom y)
|
||||
* distance between centers of entities (but bottom y)
|
||||
* only when not swept
|
||||
*/
|
||||
public Vec distance;
|
||||
|
||||
/**
|
||||
* the offset to the entity to to resolve the collision
|
||||
* only when not swept
|
||||
*/
|
||||
public Vec offset;
|
||||
|
||||
/**
|
||||
* percentage of movement until collision (only when swept)
|
||||
*/
|
||||
public double percentage;
|
||||
|
||||
/**
|
||||
* the position where the collision takes place (only when swept)
|
||||
*/
|
||||
public Vec collidedPosition;
|
||||
|
||||
/**
|
||||
* the direction and speed where an object should bounce (only when swept)
|
||||
*/
|
||||
public Vec normal;
|
||||
|
||||
/**
|
||||
* used to track what entity collided
|
||||
*/
|
||||
public Entity entity;
|
||||
}
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
package net.pivipi.physics;
|
||||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.collision.SweepResult;
|
||||
import net.minestom.server.collision.VisibleSweepResult;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
|
||||
public class CollisionDetector {
|
||||
/**
|
||||
*
|
||||
* @param entity
|
||||
* @param moving the moving entity
|
||||
* @param velocity the applied velocity
|
||||
* @return null if no collision
|
||||
*/
|
||||
public static VisibleSweepResult willCollideWithEntity(Entity entity, Entity moving, Vec movement) {
|
||||
SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0);
|
||||
boolean collided = entity.getBoundingBox().intersectBoxSwept(moving.getPosition(), movement, entity.getPosition(), moving.getBoundingBox(), sweepResult);
|
||||
return collided ? new VisibleSweepResult(sweepResult) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groundY ground Y
|
||||
* @param moving the moving entity
|
||||
* @return null if no collision
|
||||
*/
|
||||
public static VisibleSweepResult willCollideWithGround(double groundY, Entity moving, Vec movement) {
|
||||
SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0);
|
||||
boolean collided = new BoundingBox(10, 1, 10).intersectBoxSwept(moving.getPosition(), movement, moving.getPosition().withY(groundY - 1).sub(5, 0, 5), moving.getBoundingBox(), sweepResult);
|
||||
return collided ? new VisibleSweepResult(sweepResult) : null;
|
||||
}
|
||||
|
||||
public static boolean collidesWithEntity(Entity entity1, Entity entity2) {
|
||||
return entity1.getBoundingBox().intersectEntity(entity1.getPosition(), entity2);
|
||||
}
|
||||
|
||||
public static boolean collidesWithGround(double groundY, Entity entity) {
|
||||
return new BoundingBox(10, 1, 10).intersectEntity(entity.getPosition().withY(groundY - 1).sub(5, 0, 5), entity);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param blockPos the block position
|
||||
* @param blockBoundingBox the block bounding box get it with registry.shape
|
||||
* @param pushed the pushed entity
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static Vec pushOutsideBlock(Pos blockPos, BoundingBox blockBoundingBox, Entity pushed) {
|
||||
return pushOutsideBlock(blockPos, blockBoundingBox, pushed, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param blockPos the block position
|
||||
* @param blockBoundingBox the block bounding box get it with registry.shape
|
||||
* @param pushed the pushed entity
|
||||
* @param collisionData puts data in this object
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static Vec pushOutsideBlock(Pos blockPos, BoundingBox blockBoundingBox, Entity pushed, CollisionData collisionData) {
|
||||
return pushOutside(blockPos.add(0.5, 0, 0.5), blockBoundingBox, pushed.getPosition(), pushed.getBoundingBox(), collisionData); // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pusher the entity that's pushing like a player
|
||||
* @param pushed the entity that's being pushed like a ball
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static Vec pushOutside(Entity pusher, Entity pushed) {
|
||||
return pushOutside(pusher, pushed, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pusher the entity that's pushing like a player
|
||||
* @param pushed the entity that's being pushed like a ball
|
||||
* @param collisionData puts data in this object
|
||||
* @return offset you must apply to push the entity outside, null if doesn't collide
|
||||
*/
|
||||
public static Vec pushOutside(Entity pusher, Entity pushed, CollisionData collisionData) {
|
||||
return pushOutside(pusher.getPosition(), pusher.getBoundingBox(), pushed.getPosition(), pushed.getBoundingBox(), collisionData);
|
||||
/*double ballXSize = pushed.getBoundingBox().width();
|
||||
double playerXSize = pusher.getBoundingBox().width();
|
||||
double ballZSize = pushed.getBoundingBox().depth();
|
||||
double playerZSize = pusher.getBoundingBox().depth();
|
||||
double ballHeight = pushed.getBoundingBox().height();
|
||||
double playerHeight = pusher.getBoundingBox().height();
|
||||
Pos ballCenterPos = pushed.getPosition();
|
||||
Pos playerCenterPos = pusher.getPosition();
|
||||
|
||||
Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2); // corner
|
||||
Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2);
|
||||
Vec ballDistance = ballCenterPos.sub(playerCenterPos).asVec(); // center distance
|
||||
|
||||
if (collisionData != null)
|
||||
collisionData.distance = ballDistance;
|
||||
|
||||
if (ballDistance.y() > 0 && ballPos.y() > playerPos.y() + playerHeight) return null; // doesn't collide on y+
|
||||
else if (playerPos.y() > ballPos.y() + ballHeight) return null; // doesn't collide on y-
|
||||
|
||||
Vec newBallOffset = new Vec(0);
|
||||
double diffX = 0, diffZ = 0;
|
||||
|
||||
if (ballDistance.x() > 0) {
|
||||
diffX = ballPos.x() - (playerPos.x() + playerXSize);
|
||||
if (diffX < 0) // collides on x+
|
||||
newBallOffset = newBallOffset.withX(-diffX);
|
||||
} else {
|
||||
diffX = playerPos.x() - (ballPos.x() + ballXSize);
|
||||
if (diffX < 0) // collides on x-
|
||||
newBallOffset = newBallOffset.withX(diffX);
|
||||
}
|
||||
|
||||
if (ballDistance.z() > 0) {
|
||||
diffZ = ballPos.z() - (playerPos.z() + playerZSize);
|
||||
if (diffZ < 0) // collides on z+
|
||||
newBallOffset = newBallOffset.withZ(-diffZ);
|
||||
} else {
|
||||
diffZ = playerPos.z() - (ballPos.z() + ballZSize);
|
||||
if (diffZ < 0) // collides on z-
|
||||
newBallOffset = newBallOffset.withZ(diffZ);
|
||||
}
|
||||
|
||||
|
||||
if (diffX < 0 && diffZ < 0)
|
||||
return diffX < diffZ ? newBallOffset.withX(0) : newBallOffset.withZ(0);
|
||||
|
||||
return null;*/
|
||||
}
|
||||
|
||||
private static Vec pushOutside(Pos pusherPos, BoundingBox pusherBox, Pos pushedPos, BoundingBox pushedBox, CollisionData collisionData) {
|
||||
double ballXSize = pushedBox.width();
|
||||
double playerXSize = pusherBox.width();
|
||||
double ballZSize = pushedBox.depth();
|
||||
double playerZSize = pusherBox.depth();
|
||||
double ballHeight = pushedBox.height();
|
||||
double playerHeight = pusherBox.height();
|
||||
Pos ballCenterPos = pushedPos;
|
||||
Pos playerCenterPos = pusherPos;
|
||||
|
||||
Pos ballPos = ballCenterPos.sub(ballXSize / 2, 0, ballZSize / 2); // corner
|
||||
Pos playerPos = playerCenterPos.sub(playerXSize / 2, 0, playerZSize / 2);
|
||||
Vec ballDistance = ballCenterPos.sub(playerCenterPos).asVec(); // center distance
|
||||
|
||||
if (collisionData != null) // TODO move this
|
||||
collisionData.distance = ballDistance;
|
||||
|
||||
if (ballDistance.y() > 0 && ballPos.y() > playerPos.y() + playerHeight) return null; // doesn't collide on y+
|
||||
else if (playerPos.y() > ballPos.y() + ballHeight) return null; // doesn't collide on y-
|
||||
|
||||
Vec newBallOffset = new Vec(0);
|
||||
double diffX = 0, diffZ = 0;
|
||||
|
||||
if (ballDistance.x() > 0) {
|
||||
diffX = ballPos.x() - (playerPos.x() + playerXSize);
|
||||
if (diffX < 0) // collides on x+
|
||||
newBallOffset = newBallOffset.withX(-diffX);
|
||||
} else {
|
||||
diffX = playerPos.x() - (ballPos.x() + ballXSize);
|
||||
if (diffX < 0) // collides on x-
|
||||
newBallOffset = newBallOffset.withX(diffX);
|
||||
}
|
||||
|
||||
if (ballDistance.z() > 0) {
|
||||
diffZ = ballPos.z() - (playerPos.z() + playerZSize);
|
||||
if (diffZ < 0) // collides on z+
|
||||
newBallOffset = newBallOffset.withZ(-diffZ);
|
||||
} else {
|
||||
diffZ = playerPos.z() - (ballPos.z() + ballZSize);
|
||||
if (diffZ < 0) // collides on z-
|
||||
newBallOffset = newBallOffset.withZ(diffZ);
|
||||
}
|
||||
|
||||
if (diffX < 0 && diffZ < 0)
|
||||
return diffX < diffZ ? newBallOffset.withX(0) : newBallOffset.withZ(0);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,62 +1,37 @@
|
|||
package net.pivipi.physics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.collision.VisibleSweepResult;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
||||
import net.minestom.server.particle.Particle;
|
||||
import net.pivipi.ball.Ball;
|
||||
import net.pivipi.physics.aabb.AABB;
|
||||
import net.pivipi.physics.aabb.AABBEntity;
|
||||
import net.pivipi.physics.aabb.AABBPlayer;
|
||||
import net.pivipi.entity.AABB;
|
||||
import net.pivipi.entity.PhysicsEntity;
|
||||
import net.pivipi.game.PhysicsSettings;
|
||||
|
||||
public class Physics {
|
||||
private final double gravity = 9.8;
|
||||
private final double blockBounciness = 0.5;
|
||||
private final double playerHeadBounciness = 0.5;
|
||||
private final double playerSneakingHeadBounciness = 0.2;
|
||||
private final double playerBodyBounciness = 0.2;
|
||||
private final double playerSneakingBodyBounciness = 0;
|
||||
private final double playerCarryStrength = 2;
|
||||
private final double headJumpStrength = 5;
|
||||
private final double headNodStep = 20;
|
||||
private final double jumpStrength = 20;
|
||||
private final double groundFriction = 0.5;
|
||||
private final double airFriction = 0.1;
|
||||
private final AABB groundAABB = new AABB(new Vec(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE), new Vec(Double.MAX_VALUE, 64, Double.MAX_VALUE));
|
||||
private PhysicsSettings settings;
|
||||
|
||||
private final double flame = 20; // flame starts at speed
|
||||
private final double flameFreq = 100; // ms per flame
|
||||
private final double flameIncrement = 0.5; // ms per flame decrement per speed unit
|
||||
private final float flameSpread = 0.2f;
|
||||
|
||||
private final Pos[] blocksNearby = new Pos[] {
|
||||
new Pos(0, 0, 0), // bottom edge
|
||||
new Pos(0, 1, 0), // top edge
|
||||
new Pos(0.5, 0.5, 0), // x+ edge
|
||||
new Pos(-0.5, 0.5, 0), // x- edge
|
||||
new Pos(0, 0.5, 0.5), // z+ edge
|
||||
new Pos(0, 0.5, -0.5), // z- edge
|
||||
};
|
||||
private final int groundY = 64; // this should be removed when block collisions
|
||||
|
||||
private Vec velocity = new Vec(0, 0, 0);
|
||||
|
||||
private final AABBEntity entity;
|
||||
private final PhysicsEntity entity;
|
||||
private int p;
|
||||
|
||||
public Physics(AABBEntity entity) {
|
||||
public Physics(PhysicsEntity entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public void process(double delta, Set<AABBPlayer> players) {
|
||||
public void process(double delta, Set<Player> players) {
|
||||
velocity = applyPhysics(delta, players);
|
||||
entity.teleport(entity.getPosition().add(velocity.mul(delta)));
|
||||
|
||||
|
@ -75,99 +50,85 @@ public class Physics {
|
|||
}
|
||||
}
|
||||
|
||||
private Vec applyPhysics(double delta, Set<AABBPlayer> players) {
|
||||
velocity = velocity.sub(0, gravity * delta, 0).mul(1 - (airFriction * delta), 1, 1 - (airFriction * delta));
|
||||
AABB aabb = entity.getAabb().velocity(velocity.mul(delta));
|
||||
private Vec applyPhysics(double delta, Set<Player> players) {
|
||||
velocity = velocity.sub(0, settings.gravity * delta, 0).mul(1 - (settings.airFriction * delta), 1, 1 - (settings.airFriction * delta));
|
||||
ArrayList<CollisionData> collisions = new ArrayList<>();
|
||||
|
||||
double groundToi = groundAABB.timeOfImpact(aabb);
|
||||
//System.out.println(velocity.mul(delta));
|
||||
//System.out.println(aabb.getMin(false));
|
||||
if (groundToi < 1) {
|
||||
if (groundToi > 0)
|
||||
entity.move(velocity.mul(delta).mul(groundToi));
|
||||
else entity.teleport(entity.getPosition().withY(65));
|
||||
velocity = velocity.mul(1 - (groundFriction * delta), -blockBounciness, 1 - (groundFriction * delta));
|
||||
aabb.velocity(velocity);
|
||||
if (Collision.collidesWithGround(groundY, entity)) {
|
||||
entity.teleport(entity.getPosition().withY(groundY));
|
||||
velocity = velocity.mul(1 - (settings.groundFriction * delta), -settings.blockBounciness, 1 - (settings.groundFriction * delta));
|
||||
} else {
|
||||
for (Player player : players) {
|
||||
CollisionData collisionData = Collision.pushOutside(player, entity, false); // TODO predict the collision
|
||||
|
||||
if (collisionData.offset != null) {
|
||||
collisionData.entity = player;
|
||||
collisions.add(collisionData);
|
||||
}
|
||||
}
|
||||
|
||||
AABB aabb = entity.getAabb();
|
||||
int minX = aabb.getMin().blockX();
|
||||
int minY = aabb.getMin().blockY();
|
||||
int minZ = aabb.getMin().blockZ();
|
||||
int maxX = aabb.getMax().blockX();
|
||||
int maxY = aabb.getMax().blockY();
|
||||
int maxZ = aabb.getMax().blockZ();
|
||||
for (int x=minX; x<=maxX; x++) {
|
||||
for (int y=minY; y<=maxY; y++) {
|
||||
for (int z=minZ; z<=maxZ; z++) {
|
||||
if (entity.getInstance().getBlock(x, y, z).isSolid()) {
|
||||
CollisionData cd = Collision.pushOutside(new Pos(x, y, z), entity, true);
|
||||
collisions.add(cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
players.forEach(player -> {
|
||||
Vec offset = aabb.resolve(player.getAabb());
|
||||
if (offset != null) {
|
||||
entity.move(offset);
|
||||
for (CollisionData collisionData : collisions) {
|
||||
if (collisionData.entity instanceof Player) {
|
||||
handlePlayerTouch((Player) collisionData.entity, collisionData);
|
||||
} else {
|
||||
handleBlockTouch(collisionData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return velocity;
|
||||
}
|
||||
|
||||
private void handleBlockTouch(Pos blockRelativePos, int edge) {
|
||||
//Pos blockPos = entity.getPosition().sub(entity.getBoundingBox().width() / 2, 0, entity.getBoundingBox().height() / 2).add(blockRelativePos);
|
||||
Pos blockPos = entity.getPosition().add(blockRelativePos);//.withX(x -> (int)(x - entity.getBoundingBox().width() / 2)).withZ(z -> (int)(z - entity.getBoundingBox().depth() / 2)).withY(y -> (int)(y));
|
||||
//Pos blockPos = ballEdgePos.withX(x -> (int)(x)).withY(y -> (int)(y)).withZ(z -> (int)(z));
|
||||
Block block = entity.getInstance().getBlock(blockPos);
|
||||
if (block.equals(Block.AIR)) return;
|
||||
private void handleBlockTouch(CollisionData collisionData) {
|
||||
|
||||
CollisionData collisionData = new CollisionData();
|
||||
CollisionDetector.pushOutsideBlock(blockPos, new BoundingBox(1, 1, 1), entity, collisionData);
|
||||
Vec offset = collisionData.distance;
|
||||
|
||||
if (block != Block.AIR) {
|
||||
//entity.teleport(entity.getPosition().add(collisionData.distance));
|
||||
switch (edge) {
|
||||
case 0:
|
||||
case 1:
|
||||
System.out.println(edge);
|
||||
offset = offset.mul(0, 1, 0);//.withY(y -> ballEdgePos.y() - blockPos.y());
|
||||
//velocity = velocity.mul(1, -blockBounciness, 1);
|
||||
break;
|
||||
case 2:
|
||||
offset = offset.add(0.5, 0, 0);
|
||||
case 3:
|
||||
System.out.println(edge);
|
||||
offset = offset.mul(1, 0, 0);//.withX(x -> ballEdgePos.x() - blockPos.x());
|
||||
//velocity = velocity.mul(-blockBounciness, 1, 1);
|
||||
break;
|
||||
case 4:
|
||||
System.out.println("addin");
|
||||
offset = offset.add(0, 0, 0.5);
|
||||
case 5:
|
||||
System.out.println(edge);
|
||||
offset = offset.mul(0, 0, 1);//.withZ(z -> ballEdgePos.z() - blockPos.z());
|
||||
//velocity = velocity.mul(1, 1, -blockBounciness);
|
||||
break;
|
||||
}
|
||||
System.out.println(offset);
|
||||
entity.teleport(entity.getPosition().add(offset));
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePlayerTouch(Player player, Vec offset) {
|
||||
Vec distance = entity.getPosition().sub(entity.getPosition()).asVec();
|
||||
private void handlePlayerTouch(Player player, CollisionData collisionData) {
|
||||
Vec offset = collisionData.offset;
|
||||
|
||||
if (offset != null) {
|
||||
Pos movementPos = player.getPosition().sub(player.getPreviousPosition());
|
||||
Pos movementPos = player.getPosition().sub(player.getPreviousPosition());
|
||||
|
||||
boolean oppositeX = (distance.x() > 0 ? velocity.x() < 0 : velocity.x() > 0);
|
||||
boolean oppositeZ = (distance.z() > 0 ? velocity.z() < 0 : velocity.z() > 0);
|
||||
double headOffsetY = player.getBoundingBox().height() - distance.y();
|
||||
boolean oppositeX = (collisionData.distance.x() > 0 ? velocity.x() < 0 : velocity.x() > 0);
|
||||
boolean oppositeZ = (collisionData.distance.z() > 0 ? velocity.z() < 0 : velocity.z() > 0);
|
||||
double headOffsetY = player.getBoundingBox().height() - collisionData.distance.y();
|
||||
|
||||
if (headOffsetY < 0.6) { // landed on head, magic value head height
|
||||
if (movementPos.y() > 0) {
|
||||
double headPitchDiff = Math.abs(movementPos.pitch());
|
||||
System.out.println(headPitchDiff);
|
||||
addVelocity(player.getPosition().direction().mul(headPitchDiff / headNodStep).withY(headJumpStrength));
|
||||
} else velocity = velocity.mul(1, player.isSneaking() ? -playerSneakingHeadBounciness : -playerHeadBounciness, 1);
|
||||
entity.teleport(entity.getPosition().withY(y -> y + headOffsetY));
|
||||
} else {
|
||||
entity.teleport(entity.getPosition().add(offset.mul(1.1))); // magic value to make some space in case of desync
|
||||
double bodyBounciness = player.isSneaking() ? -playerSneakingBodyBounciness : -playerBodyBounciness;
|
||||
|
||||
velocity = velocity
|
||||
.withX(x -> oppositeX ? x * bodyBounciness : x + movementPos.x() * playerCarryStrength)
|
||||
.withY(y -> distance.y() < 1.0 ? movementPos.y() * jumpStrength : y) // magic value legs
|
||||
.withZ(z -> oppositeZ ? z * bodyBounciness : z + movementPos.z() * playerCarryStrength);
|
||||
}
|
||||
System.out.println(offset);
|
||||
System.out.println(oppositeX);
|
||||
System.out.println(velocity.x());
|
||||
|
||||
if (headOffsetY < 0.6) { // landed on head, magic value head height
|
||||
if (movementPos.y() > 0) {
|
||||
double headPitchDiff = Math.abs(movementPos.pitch());
|
||||
System.out.println(headPitchDiff);
|
||||
addVelocity(player.getPosition().direction().mul(headPitchDiff / settings.headNodStep).withY(settings.headJumpStrength));
|
||||
} else velocity = velocity.mul(1, player.isSneaking() ? -settings.playerSneakingHeadBounciness : -settings.playerHeadBounciness, 1);
|
||||
entity.teleport(entity.getPosition().withY(y -> y + headOffsetY));
|
||||
} else {
|
||||
entity.teleport(entity.getPosition().add(offset.mul(1.1))); // magic value to make some space in case of desync
|
||||
double bodyBounciness = player.isSneaking() ? -settings.playerSneakingBodyBounciness : -settings.playerBodyBounciness;
|
||||
|
||||
velocity = velocity
|
||||
.withX(x -> oppositeX ? x * bodyBounciness : x + movementPos.x() * settings.playerCarryStrength)
|
||||
.withY(y -> collisionData.distance.y() < 1.0 ? movementPos.y() * settings.jumpStrength : y) // magic value legs
|
||||
.withZ(z -> oppositeZ ? z * bodyBounciness : z + movementPos.z() * settings.playerCarryStrength);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
package net.pivipi.physics.aabb;
|
||||
|
||||
import net.kyori.adventure.bossbar.BossBar.Overlay;
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.collision.SweepResult;
|
||||
import net.minestom.server.collision.VisibleSweepResult;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.pivipi.physics.CollisionData;
|
||||
|
||||
public class AABB {
|
||||
Vec min, max;
|
||||
Vec velocity = Vec.ZERO;
|
||||
|
||||
public AABB(Vec min, Vec max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public Vec getMin(boolean swept) {
|
||||
if (swept) min = min.min(min.add(velocity));
|
||||
return min;
|
||||
}
|
||||
|
||||
public Vec getMax(boolean swept) {
|
||||
if (swept) max = max.min(max.add(velocity));
|
||||
return max;
|
||||
}
|
||||
|
||||
public Vec getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
public AABB velocity(Vec velocity) {
|
||||
this.velocity = velocity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if collides target
|
||||
* @param aabb the target
|
||||
* @param swept is target swept
|
||||
* @return
|
||||
*/
|
||||
public boolean collides(AABB aabb, boolean swept) {
|
||||
Vec bMin = aabb.getMin(swept);
|
||||
Vec bMax = aabb.getMax(swept);
|
||||
|
||||
return (min.x() <= bMax.x() && max.x() >= bMin.x() &&
|
||||
min.y() <= bMax.y() && max.y() >= bMin.y() &&
|
||||
min.z() <= bMax.z() && max.z() >= bMin.z());
|
||||
}
|
||||
|
||||
/**
|
||||
* time of impact with target
|
||||
* @param moving another target (moving or not)
|
||||
* @return percent of velocity moved, Double.NEGATIVE_INFINITY if already collides, Double.POSITIVE_INFINITY otherwise
|
||||
*/
|
||||
public double timeOfImpact(AABB moving) {
|
||||
if (this.collides(moving, false)) return Double.NEGATIVE_INFINITY;
|
||||
|
||||
SweepResult sweepResult = new SweepResult(1, 0, 0, 0, null, 0, 0, 0);
|
||||
Vec bb = max.sub(min);
|
||||
Vec bb2 = moving.max.sub(moving.min);
|
||||
Vec relativeVelocity = moving.velocity.sub(velocity);
|
||||
|
||||
boolean collides = new BoundingBox(bb.x(), bb.y(), bb.z()).intersectBoxSwept(moving.min, relativeVelocity, min, new BoundingBox(bb2.x(), bb2.y(), bb2.z()), sweepResult);
|
||||
if (!collides) return Double.POSITIVE_INFINITY;
|
||||
|
||||
return new VisibleSweepResult(sweepResult).res;
|
||||
|
||||
/* Vec relativeVelocity = moving.velocity.sub(velocity);
|
||||
double enterX = Double.NEGATIVE_INFINITY, exitX = Double.POSITIVE_INFINITY;
|
||||
double enterY = Double.NEGATIVE_INFINITY, exitY = Double.POSITIVE_INFINITY;
|
||||
double enterZ = Double.NEGATIVE_INFINITY, exitZ = Double.POSITIVE_INFINITY;
|
||||
boolean overlap = true;
|
||||
|
||||
if (relativeVelocity.x() != 0) {
|
||||
enterX = (moving.min.x() - max.x()) / relativeVelocity.x();
|
||||
exitX = (moving.max.x() - min.x()) / relativeVelocity.x();
|
||||
} else {
|
||||
overlap = overlap && !(max.x() < moving.min.x() || moving.max.x() < min.x());
|
||||
}
|
||||
|
||||
if (relativeVelocity.y() != 0) {
|
||||
enterY = (moving.min.y() - max.y()) / relativeVelocity.y();
|
||||
exitY = (moving.max.y() - min.y()) / relativeVelocity.y();
|
||||
} else {
|
||||
overlap = overlap && !(max.y() < moving.min.y() || moving.max.y() < min.y());
|
||||
}
|
||||
|
||||
if (relativeVelocity.z() != 0) {
|
||||
enterZ = (moving.min.z() - max.z()) / relativeVelocity.z();
|
||||
exitZ = (moving.max.z() - min.z()) / relativeVelocity.z();
|
||||
} else {
|
||||
overlap = overlap && !(max.z() < moving.min.z() || moving.max.z() < min.z());
|
||||
}
|
||||
|
||||
//if (overlap) return Double.NEGATIVE_INFINITY;
|
||||
|
||||
double entry = Math.max(Math.max(enterX, enterY), enterZ);
|
||||
double exit = Math.min(Math.min(exitX, exitY), exitZ);
|
||||
|
||||
return entry > exit ? Double.POSITIVE_INFINITY : entry; // TODO simplify*/
|
||||
}
|
||||
|
||||
/**
|
||||
* don't push myself inside
|
||||
* @param aabb the object, it can be moving
|
||||
* @return offset to move me by or null if no overlap
|
||||
*/
|
||||
public Vec resolveSwept(AABB aabb) {
|
||||
double toi = timeOfImpact(aabb);
|
||||
return toi < 0 || toi > 1 ? null : velocity.mul(toi);
|
||||
}
|
||||
|
||||
/**
|
||||
* push myself outside
|
||||
* @param aabb the static object
|
||||
* @return offset to move me by or null if no overlap
|
||||
*/
|
||||
public Vec resolve(AABB aabb) {
|
||||
Vec bMin = aabb.getMin(false);
|
||||
Vec bMax = aabb.getMax(false);
|
||||
Vec offset = Vec.ZERO;
|
||||
|
||||
Vec overlap = new Vec(
|
||||
Math.min(max.x(), bMax.x()) - Math.max(min.x(), bMin.x()),
|
||||
Math.min(max.y(), bMax.y()) - Math.max(min.y(), bMin.y()),
|
||||
Math.min(max.z(), bMax.z()) - Math.max(min.z(), bMin.z()));
|
||||
|
||||
if (overlap.x() <= 0 || overlap.y() <= 0 || overlap.z() <= 0)
|
||||
return null;
|
||||
|
||||
if (overlap.x() < overlap.y() && overlap.x() < overlap.z()) {
|
||||
if (min.x() < bMin.x())
|
||||
offset = offset.withX(x -> x - overlap.x());
|
||||
else
|
||||
offset = offset.withX(x -> x + overlap.x());
|
||||
} else if (overlap.y() < overlap.x() && overlap.y() < overlap.z()) {
|
||||
if (min.y() < bMin.y())
|
||||
offset = offset.withY(y -> y - overlap.y());
|
||||
else
|
||||
offset = offset.withY(y -> y + overlap.y());
|
||||
} else {
|
||||
if (min.z() < bMin.z())
|
||||
offset = offset.withZ(z -> z - overlap.z());
|
||||
else
|
||||
offset = offset.withZ(z -> z + overlap.z());
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package net.pivipi.physics.aabb;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
|
||||
public class AABBEntity extends Entity {
|
||||
protected AABB aabb = new AABB(Vec.ZERO, Vec.ZERO);
|
||||
|
||||
public AABBEntity(@NotNull EntityType entityType) {
|
||||
super(entityType);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
public AABB getAabb() {
|
||||
aabb.min = this.getPosition().sub(this.getBoundingBox().width() / 2, 0, this.getBoundingBox().depth() / 2).asVec();
|
||||
aabb.max = this.getPosition().add(this.getBoundingBox().width() / 2, this.getBoundingBox().height(), this.getBoundingBox().depth() / 2).asVec();
|
||||
aabb.velocity = this.getVelocity();
|
||||
return aabb;
|
||||
}
|
||||
|
||||
public void move(Vec offset) {
|
||||
this.teleport(this.getPosition().add(offset));
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package net.pivipi.physics.aabb;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.PlayerProvider;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
|
||||
public class AABBPlayer extends Player {
|
||||
protected AABB aabb = new AABB(Vec.ZERO, Vec.ZERO);
|
||||
|
||||
private Pos lastPosition = Pos.ZERO;
|
||||
private long lastUpdate;
|
||||
private double delta;
|
||||
|
||||
private Pos movement;
|
||||
|
||||
public AABBPlayer(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConnection playerConnection) {
|
||||
super(uuid, username, playerConnection);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
public AABB getAabb() {
|
||||
aabb.min = this.getPosition().sub(this.getBoundingBox().width() / 2, 0, this.getBoundingBox().depth() / 2).asVec();
|
||||
aabb.max = this.getPosition().add(this.getBoundingBox().width() / 2, this.getBoundingBox().height(), this.getBoundingBox().depth() / 2).asVec();
|
||||
aabb.velocity = this.getVelocity().mul(delta).add(movement);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(long time) {
|
||||
super.update(time);
|
||||
|
||||
this.movement = this.getPosition().sub(lastPosition);
|
||||
this.lastPosition = this.getPosition();
|
||||
|
||||
this.delta = (time - lastUpdate) / 1000.0;
|
||||
this.lastUpdate = time;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package net.pivipi.physics.aabb;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
|
||||
public class BlockAABB extends AABB {
|
||||
|
||||
public BlockAABB(Vec pos) {
|
||||
super(pos, pos.add(1));
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@ public class SoccerGenerator implements Generator {
|
|||
public void generate(@NotNull GenerationUnit unit) {
|
||||
UnitModifier modifier = unit.modifier();
|
||||
|
||||
modifier.fillBiome(Biome.CRIMSON_FOREST);
|
||||
modifier.fillBiome(Biome.PLAINS);
|
||||
|
||||
modifier.fillHeight(63, 64, Block.WHITE_WOOL);
|
||||
modifier.fill(unit.absoluteStart().withY(63).add(1, 0, 1), unit.absoluteEnd().withY(64), Block.GREEN_CONCRETE_POWDER);
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package net.pivipi.world;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.pivipi.ball.Ball;
|
||||
import net.pivipi.physics.aabb.AABBPlayer;
|
||||
|
||||
public class Stadium {
|
||||
public final Set<AABBPlayer> players = new HashSet<AABBPlayer>();
|
||||
public Ball ball;
|
||||
}
|
Loading…
Reference in a new issue