Added block breaking and refactored Controls

- Blocks can be broken by pointing at them at pressing LMB
- Rewritten ControlTriggers
- Rewritten KeyMatcher
- CollisionPathComputer now has a 0.5 margin
This commit is contained in:
OLEGSHA 2020-11-17 22:53:56 +03:00
parent 56eaec522f
commit cf18da8350
9 changed files with 378 additions and 101 deletions

View File

@ -0,0 +1,48 @@
package ru.windcorp.progressia.client.comms.controls;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.common.comms.controls.ControlData;
import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry;
import ru.windcorp.progressia.common.comms.controls.PacketControl;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class ControlTriggerLambda extends ControlTriggerInputBased {
private final String packetId;
private final Predicate<InputEvent> predicate;
private final BiConsumer<InputEvent, ControlData> dataWriter;
public ControlTriggerLambda(
String id,
Predicate<InputEvent> predicate,
BiConsumer<InputEvent, ControlData> dataWriter
) {
super(id);
this.packetId = NamespacedUtil.getId(
NamespacedUtil.getNamespace(id),
"ControlKeyPress" + NamespacedUtil.getName(id)
);
this.predicate = predicate;
this.dataWriter = dataWriter;
}
@Override
public PacketControl onInputEvent(InputEvent event) {
if (!predicate.test(event)) return null;
PacketControl packet = new PacketControl(
packetId,
ControlDataRegistry.getInstance().create(getId())
);
dataWriter.accept(event, packet.getControl());
return packet;
}
}

View File

@ -1,40 +0,0 @@
package ru.windcorp.progressia.client.comms.controls;
import java.util.function.Predicate;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry;
import ru.windcorp.progressia.common.comms.controls.PacketControl;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class ControlTriggerOnKeyPress extends ControlTriggerInputBased {
private final Predicate<KeyEvent> predicate;
private final PacketControl packet;
public ControlTriggerOnKeyPress(
String id,
Predicate<KeyEvent> predicate
) {
super(id);
this.predicate = predicate;
this.packet = new PacketControl(
NamespacedUtil.getId(getNamespace(), "ControlKeyPress" + getName()),
ControlDataRegistry.getInstance().get(getId())
);
}
@Override
public PacketControl onInputEvent(InputEvent event) {
if (!(event instanceof KeyEvent)) return null;
KeyEvent keyEvent = (KeyEvent) event;
if (!keyEvent.isPress()) return null;
if (!predicate.test(keyEvent)) return null;
return packet;
}
}

View File

@ -0,0 +1,176 @@
package ru.windcorp.progressia.client.comms.controls;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.common.comms.controls.ControlData;
public class ControlTriggers {
public static ControlTriggerInputBased of(
String id,
BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent> predicate
) {
return new ControlTriggerLambda(id, predicate, dataWriter);
}
public static ControlTriggerInputBased of(
String id,
Consumer<ControlData> dataWriter,
Predicate<InputEvent> predicate
) {
return of(
id,
(input, control) -> dataWriter.accept(control),
predicate
);
}
public static ControlTriggerInputBased of(
String id,
Predicate<InputEvent> predicate
) {
return of(
id,
(input, control) -> {},
predicate
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
BiConsumer<I, ControlData> dataWriter,
Predicate<I>... predicates
) {
return of(
id,
createCheckedDataWriter(inputType, dataWriter),
createCheckedCompoundPredicate(inputType, predicates)
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
Consumer<ControlData> dataWriter,
Predicate<I>... predicates
) {
return of(
id,
inputType,
(input, control) -> dataWriter.accept(control),
predicates
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
Predicate<I>... predicates
) {
return of(
id,
(input, control) -> {},
createCheckedCompoundPredicate(inputType, predicates)
);
}
@SafeVarargs
public static ControlTriggerInputBased of(
String id,
BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent>... predicates
) {
return of(
id,
InputEvent.class,
dataWriter,
predicates
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Consumer<ControlData> dataWriter,
Predicate<InputEvent>... predicates
) {
return of(
id,
(input, control) -> dataWriter.accept(control),
predicates
);
}
@SafeVarargs
public static ControlTriggerInputBased of(
String id,
Predicate<InputEvent>... predicates
) {
return of(
id,
InputEvent.class,
(input, control) -> {},
predicates
);
}
private static
<I extends InputEvent>
BiConsumer<InputEvent, ControlData>
createCheckedDataWriter(
Class<I> inputType,
BiConsumer<I, ControlData> dataWriter
) {
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
}
private static
<I extends InputEvent>
Predicate<InputEvent>
createCheckedCompoundPredicate(
Class<I> inputType,
Predicate<I>[] predicates
) {
return new CompoundCastPredicate<>(inputType, predicates);
}
private static class CompoundCastPredicate<I extends InputEvent> implements Predicate<InputEvent> {
private final Class<I> inputType;
private final Predicate<I>[] predicates;
public CompoundCastPredicate(Class<I> inputType, Predicate<I>[] predicates) {
this.inputType = inputType;
this.predicates = predicates;
}
@Override
public boolean test(InputEvent inputEvent) {
if (!inputType.isInstance(inputEvent)) {
return false;
}
I castEvent = inputType.cast(inputEvent);
for (Predicate<I> predicate : predicates) {
if (!predicate.test(castEvent)) {
return false;
}
}
return true;
}
}
private ControlTriggers() {}
}

View File

@ -17,46 +17,24 @@
*******************************************************************************/
package ru.windcorp.progressia.client.graphics.input;
import gnu.trove.set.TIntSet;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import java.util.function.Predicate;
import org.lwjgl.glfw.GLFW;
public class KeyMatcher {
private final int key;
private final int[] additionalKeys;
private final int mods;
public KeyMatcher(int key, int[] additionalKeys, int mods) {
protected KeyMatcher(int key, int mods) {
this.key = key;
this.additionalKeys = additionalKeys;
this.mods = mods;
}
public KeyMatcher(KeyEvent template, int... additionalKeys) {
this(template.getKey(), additionalKeys, template.getMods());
}
public static KeyMatcher createKeyMatcher(KeyEvent template) {
return new KeyMatcher(
template,
InputTracker.getPressedKeys().toArray()
);
}
public boolean matches(KeyEvent event) {
if (!event.isPress()) return false;
if (event.getKey() != getKey()) return false;
if (event.getMods() != getMods()) return false;
TIntSet pressedKeys = InputTracker.getPressedKeys();
if (pressedKeys.size() != additionalKeys.length) return false;
for (int additionalKey : additionalKeys) {
if (!pressedKeys.contains(additionalKey)) {
return false;
}
}
if ((event.getMods() & getMods()) != getMods()) return false;
return true;
}
@ -65,12 +43,52 @@ public class KeyMatcher {
return key;
}
public int[] getAdditionalKeys() {
return additionalKeys;
}
public int getMods() {
return mods;
}
public static KeyMatcher.Builder of(int key) {
return new KeyMatcher.Builder(key);
}
public static class Builder {
private final int key;
private int mods = 0;
public Builder(int key) {
this.key = key;
}
public Builder with(int modifier) {
this.mods += modifier;
return this;
}
public Builder withShift() {
return with(GLFW.GLFW_MOD_SHIFT);
}
public Builder withCtrl() {
return with(GLFW.GLFW_MOD_CONTROL);
}
public Builder withAlt() {
return with(GLFW.GLFW_MOD_ALT);
}
public Builder withSuper() {
return with(GLFW.GLFW_MOD_SUPER);
}
public KeyMatcher build() {
return new KeyMatcher(key, mods);
}
public Predicate<KeyEvent> matcher() {
return build()::matches;
}
}
}

View File

@ -10,6 +10,8 @@ import static java.lang.Math.*;
public class CollisionPathComputer {
private static final float PADDING = 0.5f;
public static void forEveryBlockInCollisionPath(
Collideable coll,
float maxTime,
@ -52,18 +54,18 @@ public class CollisionPathComputer {
Vec3i pos = Vectors.grab3i();
for (
pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x));
pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x));
pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x) - PADDING);
pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x) + PADDING);
pos.x += 1
) {
for (
pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y));
pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y));
pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y) - PADDING);
pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y) + PADDING);
pos.y += 1
) {
for (
pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z));
pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z));
pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z) - PADDING);
pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z) + PADDING);
pos.z += 1
) {
action.accept(pos);

View File

@ -1,8 +1,8 @@
package ru.windcorp.progressia.common.comms.controls;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry;
public class ControlDataRegistry extends NamespacedInstanceRegistry<ControlData> {
public class ControlDataRegistry extends NamespacedFactoryRegistry<ControlData> {
private static final ControlDataRegistry INSTANCE = new ControlDataRegistry();

View File

@ -6,6 +6,15 @@ import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.Client;
public abstract class ControlLogic extends Namespaced {
@FunctionalInterface
public static interface Lambda {
void apply(
Server server,
PacketControl packet,
Client client
);
}
public ControlLogic(String id) {
super(id);
@ -16,5 +25,14 @@ public abstract class ControlLogic extends Namespaced {
PacketControl packet,
Client client
);
public static ControlLogic of(String id, Lambda logic) {
return new ControlLogic(id) {
@Override
public void apply(Server server, PacketControl packet, Client client) {
logic.apply(server, packet, client);
}
};
}
}

View File

@ -0,0 +1,22 @@
package ru.windcorp.progressia.test;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.comms.controls.ControlData;
public class ControlBreakBlockData extends ControlData {
private final Vec3i blockInWorld = new Vec3i();
public ControlBreakBlockData(String id) {
super(id);
}
public Vec3i getBlockInWorld() {
return blockInWorld;
}
public void setBlockInWorld(Vec3i blockInWorld) {
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
}
}

View File

@ -8,8 +8,11 @@ import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.comms.controls.*;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.world.block.*;
import ru.windcorp.progressia.client.world.entity.*;
import ru.windcorp.progressia.client.world.tile.*;
@ -21,8 +24,6 @@ import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.block.*;
import ru.windcorp.progressia.common.world.entity.*;
import ru.windcorp.progressia.common.world.tile.*;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.Client;
import ru.windcorp.progressia.server.comms.controls.*;
import ru.windcorp.progressia.server.world.block.*;
import ru.windcorp.progressia.server.world.entity.*;
@ -88,7 +89,7 @@ public class TestContent {
private static void registerEntities() {
float scale = 1.8f / 8;
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f)));
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.8f, 0.8f, 1.8f)));
register(new TestEntityRenderHuman("Test:Player"));
register(new EntityLogic("Test:Player"));
@ -98,25 +99,43 @@ public class TestContent {
}
private static void regsiterControls() {
ControlDataRegistry.getInstance().register(new ControlData("Test:Switch000"));
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test:Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches));
ControlLogicRegistry.getInstance().register(new ControlLogic("Test:Switch000") {
@Override
public void apply(Server server, PacketControl packet, Client client) {
Vec3i z000 = new Vec3i(0, 0, 0);
ChunkData data = server.getWorld().getChunk(z000).getData();
BlockData block;
if (data.getBlock(z000).getId().equals("Test:Stone")) {
block = BlockDataRegistry.getInstance().get("Test:Glass");
} else {
block = BlockDataRegistry.getInstance().get("Test:Stone");
}
server.getAdHocChanger().setBlock(z000, block);
ControlDataRegistry data = ControlDataRegistry.getInstance();
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
ControlLogicRegistry logic = ControlLogicRegistry.getInstance();
data.register("Test:Switch000", ControlData::new);
triggers.register(ControlTriggers.of(
"Test:Switch000",
KeyEvent.class,
KeyMatcher.of(GLFW.GLFW_KEY_H).matcher()
));
logic.register(ControlLogic.of("Test:Switch000", (server, packet, client) -> {
Vec3i z000 = new Vec3i(0, 0, 0);
ChunkData chunk = server.getWorld().getChunk(z000).getData();
BlockData block;
if (chunk.getBlock(z000).getId().equals("Test:Stone")) {
block = BlockDataRegistry.getInstance().get("Test:Glass");
} else {
block = BlockDataRegistry.getInstance().get("Test:Stone");
}
});
server.getAdHocChanger().setBlock(z000, block);
}));
data.register("Test:BreakBlock", ControlBreakBlockData::new);
triggers.register(ControlTriggers.of(
"Test:BreakBlock",
KeyEvent.class,
TestContent::onBlockBreakTrigger,
KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(),
i -> getLookingAt() != null
));
logic.register(ControlLogic.of("Test:BreakBlock", (server, packet, client) -> {
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air"));
}));
}
private static void register(BlockData x) {
@ -171,5 +190,19 @@ public class TestContent {
private static void register(EntityLogic x) {
EntityLogicRegistry.getInstance().register(x);
}
private static Vec3i getLookingAt() {
ru.windcorp.progressia.client.Client client = ClientState.getInstance();
if (client == null) return null;
LocalPlayer player = client.getLocalPlayer();
if (player == null) return null;
return player.getLookingAt();
}
private static void onBlockBreakTrigger(ControlData control) {
((ControlBreakBlockData) control).setBlockInWorld(getLookingAt());
}
}