Android networking reworked

This commit is contained in:
DBotThePony 2022-08-17 17:02:07 +07:00
parent 20425015ea
commit 2f6f40d5f4
Signed by: DBot
GPG Key ID: DCC23B5715498507
24 changed files with 559 additions and 548 deletions

View File

@ -21,7 +21,7 @@ import ru.dbotthepony.mc.otm.capability.android.AndroidCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer; import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.drive.DrivePool; import ru.dbotthepony.mc.otm.capability.drive.DrivePool;
import ru.dbotthepony.mc.otm.client.AndroidGui; import ru.dbotthepony.mc.otm.client.AndroidGui;
import ru.dbotthepony.mc.otm.client.EventHandler; import ru.dbotthepony.mc.otm.client.EventHandlerKt;
import ru.dbotthepony.mc.otm.client.model.GravitationStabilizerModel; import ru.dbotthepony.mc.otm.client.model.GravitationStabilizerModel;
import ru.dbotthepony.mc.otm.client.model.TritaniumArmorModel; import ru.dbotthepony.mc.otm.client.model.TritaniumArmorModel;
import ru.dbotthepony.mc.otm.compat.mekanism.QIOKt; import ru.dbotthepony.mc.otm.compat.mekanism.QIOKt;
@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.item.weapon.AbstractWeaponItem;
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem; import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem;
import ru.dbotthepony.mc.otm.matter.MatterDataKt; import ru.dbotthepony.mc.otm.matter.MatterDataKt;
import ru.dbotthepony.mc.otm.matter.MatterRegistryKt; import ru.dbotthepony.mc.otm.matter.MatterRegistryKt;
import ru.dbotthepony.mc.otm.network.AndroidNetworkChannel;
import ru.dbotthepony.mc.otm.network.MatteryNetworking; import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.registry.*; import ru.dbotthepony.mc.otm.registry.*;
import ru.dbotthepony.mc.otm.storage.*; import ru.dbotthepony.mc.otm.storage.*;
@ -108,6 +109,8 @@ public final class OverdriveThatMatters {
AndroidFeatureType.Companion.markRegistryLoaded(); AndroidFeatureType.Companion.markRegistryLoaded();
MatteryNetworking.register(); MatteryNetworking.register();
AndroidNetworkChannel.INSTANCE.register();
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125")); ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"));
if (ModList.get().isLoaded("mekanism")) { if (ModList.get().isLoaded("mekanism")) {
@ -120,7 +123,7 @@ public final class OverdriveThatMatters {
ANDROID_GUI = new AndroidGui(); ANDROID_GUI = new AndroidGui();
MinecraftForge.EVENT_BUS.register(ANDROID_GUI); MinecraftForge.EVENT_BUS.register(ANDROID_GUI);
MinecraftForge.EVENT_BUS.register(AndroidGui.class); MinecraftForge.EVENT_BUS.register(AndroidGui.class);
MinecraftForge.EVENT_BUS.register(EventHandler.class); MinecraftForge.EVENT_BUS.register(EventHandlerKt.class);
TritaniumArmorModel.register(); TritaniumArmorModel.register();
GravitationStabilizerModel.register(); GravitationStabilizerModel.register();

View File

@ -71,7 +71,7 @@ public class AndroidGui {
} }
mc.player.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> { mc.player.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (!((AndroidCapabilityPlayer) cap).willBecomeAndroid) { if (!((AndroidCapabilityPlayer) cap).getWillBecomeAndroid()) {
known_button.x = known_button_x; known_button.x = known_button_x;
known_button.y = known_button_y; known_button.y = known_button_y;
known_button_x = -1; known_button_x = -1;
@ -81,7 +81,7 @@ public class AndroidGui {
return; return;
} }
int dispersion = (int) ((10.0 * Math.max(0, ((AndroidCapabilityPlayer) cap).sleep_ticks - 20)) / (AndroidCapabilityPlayer.SLEEP_TICKS_LIMIT - 20)); int dispersion = (int) ((10.0 * Math.max(0, ((AndroidCapabilityPlayer) cap).getSleepTicks() - 20)) / (AndroidCapabilityPlayer.SLEEP_TICKS_LIMIT - 20));
known_button.x = known_button_x - dispersion / 2 + (int) (button_shaker.nextDouble() * dispersion); known_button.x = known_button_x - dispersion / 2 + (int) (button_shaker.nextDouble() * dispersion);
known_button.y = known_button_y - dispersion / 2 + (int) (button_shaker.nextDouble() * dispersion); known_button.y = known_button_y - dispersion / 2 + (int) (button_shaker.nextDouble() * dispersion);
@ -110,7 +110,7 @@ public class AndroidGui {
known_button_screen = screen; known_button_screen = screen;
mc.player.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> { mc.player.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (((AndroidCapabilityPlayer) cap).willBecomeAndroid) { if (((AndroidCapabilityPlayer) cap).getWillBecomeAndroid()) {
known_button_screen = screen; known_button_screen = screen;
} }
}); });

View File

@ -1,44 +0,0 @@
package ru.dbotthepony.mc.otm.client;
import net.minecraftforge.client.event.MovementInputUpdateEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.registry.AndroidFeatures;
public class EventHandler {
@SubscribeEvent
public static void inputEvent(MovementInputUpdateEvent event) {
var ply = event.getPlayer();
var input = event.getInput();
ply.getCapability(MatteryCapability.ANDROID).ifPresent(_cap -> {
if (!(_cap instanceof AndroidCapabilityPlayer cap))
return;
if (cap.hasFeature(AndroidFeatures.INSTANCE.getAIR_BAGS()))
return;
if (ply.getAbilities().mayfly) {
cap.last_jump_ticks = 14;
} else {
if (ply.isInWater()) {
if (ply.isOnGround()) {
cap.last_jump_ticks = 14;
}
if (ply.isSwimming()) {
ply.setSwimming(false);
}
if (cap.last_jump_ticks <= 0) {
event.getInput().jumping = false;
event.getInput().up = false;
} else {
cap.last_jump_ticks--;
}
}
}
});
}
}

View File

@ -16,12 +16,10 @@ import ru.dbotthepony.mc.otm.matter.RegistryPacketClear;
import ru.dbotthepony.mc.otm.matter.RegistryPacketFullUpdate; import ru.dbotthepony.mc.otm.matter.RegistryPacketFullUpdate;
import ru.dbotthepony.mc.otm.matter.RegistryPacketRemove; import ru.dbotthepony.mc.otm.matter.RegistryPacketRemove;
import ru.dbotthepony.mc.otm.matter.RegistryPacketUpdate; import ru.dbotthepony.mc.otm.matter.RegistryPacketUpdate;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.menu.data.*; import ru.dbotthepony.mc.otm.menu.data.*;
import ru.dbotthepony.mc.otm.menu.widget.BooleanPlayerInputPacket; import ru.dbotthepony.mc.otm.menu.widget.BooleanPlayerInputPacket;
import ru.dbotthepony.mc.otm.menu.widget.NumberPlayerInputPacket; import ru.dbotthepony.mc.otm.menu.widget.NumberPlayerInputPacket;
import ru.dbotthepony.mc.otm.menu.widget.OneWayPlayerInputPacket; import ru.dbotthepony.mc.otm.menu.widget.OneWayPlayerInputPacket;
import ru.dbotthepony.mc.otm.network.android.*;
import java.util.Optional; import java.util.Optional;
@ -54,42 +52,6 @@ public class MatteryNetworking {
} }
public static void register() { public static void register() {
CHANNEL.registerMessage(
next_network_id++,
AndroidBatteryPacket.class,
AndroidBatteryPacket::write,
AndroidBatteryPacket::read,
AndroidBatteryPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
AndroidEnergyPacket.class,
AndroidEnergyPacket::write,
AndroidEnergyPacket::read,
AndroidEnergyPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
AndroidStatusPacket.class,
AndroidStatusPacket::write,
AndroidStatusPacket::read,
AndroidStatusPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
AndroidFeaturePacket.class,
AndroidFeaturePacket::write,
AndroidFeaturePacket::read,
AndroidFeaturePacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage( CHANNEL.registerMessage(
next_network_id++, next_network_id++,
PatternGridPacket.class, PatternGridPacket.class,
@ -162,24 +124,6 @@ public class MatteryNetworking {
Optional.of(NetworkDirection.PLAY_TO_CLIENT) Optional.of(NetworkDirection.PLAY_TO_CLIENT)
); );
CHANNEL.registerMessage(
next_network_id++,
AndroidResearchRequestPacket.class,
AndroidResearchRequestPacket::write,
AndroidResearchRequestPacket::read,
AndroidResearchRequestPacket::play,
Optional.of(NetworkDirection.PLAY_TO_SERVER)
);
CHANNEL.registerMessage(
next_network_id++,
AndroidResearchPacket.class,
AndroidResearchPacket::write,
AndroidResearchPacket::read,
AndroidResearchPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage( CHANNEL.registerMessage(
next_network_id++, next_network_id++,
ClearPacket.class, ClearPacket.class,

View File

@ -1,36 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import java.util.function.Supplier;
public record AndroidBatteryPacket(ItemStack battery) {
public void write(FriendlyByteBuf buffer) {
buffer.writeItem(battery);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> cap.setBatteryItemStack(battery));
}
}
public static AndroidBatteryPacket read(FriendlyByteBuf buffer) {
return new AndroidBatteryPacket(buffer.readItem());
}
}

View File

@ -1,43 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.core.ImpreciseFraction;
import java.util.function.Supplier;
public record AndroidEnergyPacket(boolean is_maximal, ImpreciseFraction energy) {
public void write(FriendlyByteBuf buffer) {
buffer.writeBoolean(is_maximal);
energy.write(buffer);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (is_maximal) {
cap.setMaxEnergy(energy);
} else {
cap.setEnergy(energy);
}
});
}
}
public static AndroidEnergyPacket read(FriendlyByteBuf buffer) {
return new AndroidEnergyPacket(buffer.readBoolean(), ImpreciseFraction.read(buffer));
}
}

View File

@ -1,52 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.registry.MRegistry;
import java.util.Objects;
import java.util.function.Supplier;
public record AndroidFeaturePacket(boolean is_added, AndroidFeatureType<?> feature) {
public AndroidFeaturePacket(boolean is_added, AndroidFeature feature) {
this(is_added, feature.getType());
}
public void write(FriendlyByteBuf buffer) {
buffer.writeBoolean(is_added);
buffer.writeInt(MRegistry.INSTANCE.getANDROID_FEATURES().getID(feature));
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (cap instanceof AndroidCapabilityPlayer pcap) {
pcap.addFeature(feature);
}
});
}
}
public static AndroidFeaturePacket read(FriendlyByteBuf buffer) {
var is_added = buffer.readBoolean();
var feature = Objects.requireNonNull(MRegistry.INSTANCE.getANDROID_FEATURES().getValue(buffer.readInt()));
return new AndroidFeaturePacket(is_added, feature);
}
}

View File

@ -1,49 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.android.AndroidResearch;
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.registry.MRegistry;
import java.util.function.Supplier;
public record AndroidResearchPacket(AndroidResearchType<?> type, CompoundTag payload) {
public AndroidResearchPacket(AndroidResearch research) {
this(research.getType(), research.serializeNBT());
}
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(MRegistry.INSTANCE.getANDROID_RESEARCH().getID(type));
buffer.writeNbt(payload);
}
public static AndroidResearchPacket read(FriendlyByteBuf buffer) {
return new AndroidResearchPacket(MRegistry.INSTANCE.getANDROID_RESEARCH().getValue(buffer.readInt()), buffer.readNbt());
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (cap instanceof AndroidCapabilityPlayer pcap) {
pcap.getResearch(type).deserializeNBT(payload);
}
});
}
}
}

View File

@ -1,45 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.android.AndroidResearch;
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;
import ru.dbotthepony.mc.otm.registry.MRegistry;
import java.util.Objects;
import java.util.function.Supplier;
public record AndroidResearchRequestPacket(AndroidResearchType<?> research) {
public AndroidResearchRequestPacket(AndroidResearchType<?> research) {
this.research = research;
}
public AndroidResearchRequestPacket(AndroidResearch feature) {
this(feature.getType());
}
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(MRegistry.INSTANCE.getANDROID_RESEARCH().getID(research));
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> context.get().getSender().getCapability(MatteryCapability.ANDROID).ifPresent(cap -> this.playServer((AndroidCapabilityPlayer) cap, context.get().getSender())));
}
public void playServer(AndroidCapabilityPlayer cap, ServerPlayer ply) {
if (!cap.isAndroid())
return;
if (ply.containerMenu instanceof AndroidStationMenu)
cap.getResearch(research).research(false);
}
public static AndroidResearchRequestPacket read(FriendlyByteBuf buffer) {
return new AndroidResearchRequestPacket(Objects.requireNonNull(MRegistry.INSTANCE.getANDROID_RESEARCH().getValue(buffer.readInt())));
}
}

View File

@ -1,56 +0,0 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.sounds.SoundSource;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.registry.MSoundEvents;
import java.util.function.Supplier;
public record AndroidStatusPacket(Type type, boolean status, boolean playSound) {
public enum Type {
IS_ANDROID,
WILL_BECOME_ANDROID,
}
public void write(FriendlyByteBuf buffer) {
buffer.writeByte(type.ordinal());
buffer.writeBoolean(status);
buffer.writeBoolean(playSound);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (cap instanceof AndroidCapabilityPlayer pcap) {
if (type == Type.IS_ANDROID)
pcap.isAndroid = status;
else if (type == Type.WILL_BECOME_ANDROID)
pcap.willBecomeAndroid = status;
}
if (playSound) {
ply.level.playSound(ply, ply, MSoundEvents.INSTANCE.getPLAYER_BECOME_ANDROID(), SoundSource.PLAYERS, 1f, 1f);
}
});
}
}
public static AndroidStatusPacket read(FriendlyByteBuf buffer) {
return new AndroidStatusPacket(Type.values()[buffer.readByte()], buffer.readBoolean(), buffer.readBoolean());
}
}

View File

@ -0,0 +1,37 @@
package ru.dbotthepony.mc.otm
import io.netty.buffer.ByteBufInputStream
import io.netty.handler.codec.EncoderException
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtIo
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
// But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement
// your own InputStream and OutputStream, since ByteBuf is meant to be operated on most time like a stream anyway?
// netty ByteBuf -> netty ByteBufInputStream -> Minecraft FriendlyInputStream
fun <T : OutputStream> T.writeNbt(value: CompoundTag): T {
try {
NbtIo.write(value, if (this is DataOutputStream) this else DataOutputStream(this))
} catch (ioexception: IOException) {
throw EncoderException(ioexception)
}
return this
}
fun <T : InputStream> T.readNbt(accounter: NbtAccounter = NbtAccounter.UNLIMITED): CompoundTag {
return try {
NbtIo.read(if (this is DataInputStream) this else DataInputStream(this), accounter)
} catch (ioexception: IOException) {
throw EncoderException(ioexception)
}
}

View File

@ -4,11 +4,20 @@ import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.event.entity.living.LivingHurtEvent import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.readNbt
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.writeNbt
import java.io.InputStream
import java.io.OutputStream
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: AndroidCapability) : INBTSerializable<CompoundTag> { abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: AndroidCapability) : INBTSerializable<CompoundTag> {
val entity get() = android.entity val entity get() = android.entity
/**
* Whenever there are changes to network
*/
var isDirty = false
open var level = 0 open var level = 0
set(value) { set(value) {
if (value != field) { if (value != field) {
@ -30,6 +39,14 @@ abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: Andr
open fun onHurt(event: LivingHurtEvent) {} open fun onHurt(event: LivingHurtEvent) {}
open fun writeNetwork(stream: OutputStream) {
stream.writeNbt(serializeNBT())
}
open fun readNetwork(stream: InputStream) {
deserializeNBT(stream.readNbt())
}
override fun serializeNBT(): CompoundTag { override fun serializeNBT(): CompoundTag {
return CompoundTag().also { return CompoundTag().also {
it["level"] = level it["level"] = level

View File

@ -3,20 +3,33 @@ package ru.dbotthepony.mc.otm.android
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.client.render.SkinElement import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.readNbt
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.writeNbt
import java.io.InputStream
import java.io.OutputStream
abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: AndroidCapabilityPlayer) : INBTSerializable<CompoundTag> { abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: AndroidCapabilityPlayer) : INBTSerializable<CompoundTag> {
var isResearched = false var isResearched = false
protected set protected set
/**
* Whenever there are changes to network
*/
var isDirty = false var isDirty = false
/**
* Called when it is required to network everything again
*/
abstract fun invalidateNetwork()
fun unResearch() { fun unResearch() {
if (!isResearched) { if (!isResearched) {
return return
@ -46,6 +59,14 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability:
*/ */
abstract fun refund(simulate: Boolean): Boolean abstract fun refund(simulate: Boolean): Boolean
open fun writeNetwork(buff: OutputStream) {
buff.writeNbt(serializeNBT())
}
open fun readNetwork(buff: InputStream) {
deserializeNBT(buff.readNbt())
}
open val canResearch: Boolean get() { open val canResearch: Boolean get() {
if (!consumeResearchCost(simulate = true)) { if (!consumeResearchCost(simulate = true)) {
return false return false

View File

@ -174,6 +174,10 @@ class AndroidResearchBuilder(
return object : AndroidResearchType<AndroidResearch>(factory@{ it, capability -> return object : AndroidResearchType<AndroidResearch>(factory@{ it, capability ->
return@factory object : AndroidResearch(it, capability) { return@factory object : AndroidResearch(it, capability) {
override fun invalidateNetwork() {
// NO-OP
}
override fun onUnResearch() { override fun onUnResearch() {
for (feature in features) { for (feature in features) {
val level = oldResearchLevel[feature.feature] val level = oldResearchLevel[feature.feature]

View File

@ -31,7 +31,7 @@ class AndroidStationBlock : MatteryBlock(), EntityBlock {
): InteractionResult { ): InteractionResult {
val cap = ply.getCapability(MatteryCapability.ANDROID).orNull() ?: return InteractionResult.FAIL val cap = ply.getCapability(MatteryCapability.ANDROID).orNull() ?: return InteractionResult.FAIL
if (!cap.isAndroid()) if (!cap.isAndroid)
return InteractionResult.FAIL return InteractionResult.FAIL
return super.use(blockState, level, blockPos, ply, hand, blockHitResult) return super.use(blockState, level, blockPos, ply, hand, blockHitResult)

View File

@ -39,7 +39,7 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
for (ent in level.getEntitiesOfClass(LivingEntity::class.java, AABB(x, y, z, x + 1.0, y + 2.0, z + 1.0))) { for (ent in level.getEntitiesOfClass(LivingEntity::class.java, AABB(x, y, z, x + 1.0, y + 2.0, z + 1.0))) {
ent.getCapability(MatteryCapability.ANDROID).ifPresent { ent.getCapability(MatteryCapability.ANDROID).ifPresent {
if (!it.isAndroid()) if (!it.isAndroid)
return@ifPresent return@ifPresent
val missing = it.missingPower val missing = it.missingPower

View File

@ -12,15 +12,12 @@ import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidFeature import ru.dbotthepony.mc.otm.android.AndroidFeature
import java.lang.Runnable import java.lang.Runnable
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.network.android.AndroidFeaturePacket
import net.minecraftforge.event.entity.living.LivingHurtEvent import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.MatteryNetworking
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket
import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket
import net.minecraft.world.effect.MobEffect import net.minecraft.world.effect.MobEffect
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.energy.CapabilityEnergy
@ -37,6 +34,10 @@ import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.ifPresentK import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.network.AndroidFeatureRemovePacket
import ru.dbotthepony.mc.otm.network.AndroidFeatureSyncPacket
import ru.dbotthepony.mc.otm.network.AndroidNetworkChannel
import ru.dbotthepony.mc.otm.orNull
import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.StatNames import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
@ -48,14 +49,10 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
override var batteryItemStack: ItemStack = ItemStack.EMPTY override var batteryItemStack: ItemStack = ItemStack.EMPTY
private var remoteBattery = ImpreciseFraction(-1)
private var remoteMaxBattery = ImpreciseFraction(-1)
private var remoteBatteryStack = ItemStack.EMPTY
protected val features = Object2ObjectArrayMap<AndroidFeatureType<*>, AndroidFeature>() protected val features = Object2ObjectArrayMap<AndroidFeatureType<*>, AndroidFeature>()
protected val networkQueue = ArrayList<Any>() protected val networkQueue = ArrayList<Any>()
protected val queuedTicks = ArrayList<Runnable>() protected val queuedTicks = ArrayList<Runnable>()
protected var networkTickedOnce = false protected var tickedOnce = false
protected fun addFeature(feature: AndroidFeature): Boolean { protected fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false if (features.containsKey(feature.type)) return false
@ -66,7 +63,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
} }
if (entity is ServerPlayer) { if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, feature)) sendNetwork(AndroidFeatureSyncPacket(feature))
} }
return true return true
@ -86,7 +83,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
} }
if (entity is ServerPlayer) { if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, factory)) sendNetwork(AndroidFeatureSyncPacket(factory))
} }
return factory return factory
@ -103,7 +100,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
} }
if (entity is ServerPlayer) { if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(false, removed)) sendNetwork(AndroidFeatureRemovePacket(removed.type))
} }
} }
@ -124,27 +121,6 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
return features[feature] as T? return features[feature] as T?
} }
override fun invalidateNetworkState() {
remoteBattery = ImpreciseFraction.MINUS_ONE
remoteMaxBattery = ImpreciseFraction.MINUS_ONE
remoteBatteryStack = ItemStack.EMPTY
if (entity is ServerPlayer) {
for (feature in features.values) {
sendNetwork(AndroidFeaturePacket(true, feature))
feature.invalidateNetwork()
}
}
}
override fun setEnergy(value: ImpreciseFraction) {
battery = value
}
override fun setMaxEnergy(value: ImpreciseFraction) {
maxBattery = value
}
override fun onHurt(event: LivingHurtEvent) { override fun onHurt(event: LivingHurtEvent) {
for (feature in features.values) { for (feature in features.values) {
feature.onHurt(event) feature.onHurt(event)
@ -216,43 +192,14 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
protected fun sendNetwork(packet: Any) { protected fun sendNetwork(packet: Any) {
if (entity is ServerPlayer) { if (entity is ServerPlayer) {
if (networkTickedOnce) { if (tickedOnce) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { entity }, packet) AndroidNetworkChannel.send(entity, packet)
} else { } else {
networkQueue.add(packet) networkQueue.add(packet)
} }
} }
} }
protected open fun tickNetwork() {
networkTickedOnce = true
if (entity is ServerPlayer) {
if (battery != remoteBattery) {
remoteBattery = battery
sendNetwork(AndroidEnergyPacket(false, battery))
}
if (maxBattery != remoteMaxBattery) {
remoteMaxBattery = maxBattery
sendNetwork(AndroidEnergyPacket(true, maxBattery))
}
if (!remoteBatteryStack.equals(batteryItemStack, false)) {
remoteBatteryStack = batteryItemStack.copy()
sendNetwork(AndroidBatteryPacket(batteryItemStack))
}
if (networkQueue.size != 0) {
for (packet in networkQueue) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { entity }, packet)
}
networkQueue.clear()
}
}
}
protected open fun tickInnerClient() {} protected open fun tickInnerClient() {}
protected open fun tickInnerClientAlways() {} protected open fun tickInnerClientAlways() {}
@ -264,7 +211,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
tickInnerClientAlways() tickInnerClientAlways()
if (isAndroid()) { if (isAndroid) {
tickInnerClient() tickInnerClient()
for (feature in features.values) { for (feature in features.values) {
@ -278,7 +225,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
tickServerAlways() tickServerAlways()
if (isAndroid()) { if (isAndroid) {
tickServer() tickServer()
for (feature in features.values) { for (feature in features.values) {
@ -291,7 +238,6 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
} }
queuedTicks.clear() queuedTicks.clear()
tickNetwork()
} }
protected open fun tickServerAlways() {} protected open fun tickServerAlways() {}
@ -396,7 +342,8 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
return receiveEnergyOuter(howMuch, simulate) return receiveEnergyOuter(howMuch, simulate)
} }
override val batteryLevel: ImpreciseFraction get() { override var batteryLevel: ImpreciseFraction
get() {
if (!batteryItemStack.isEmpty) { if (!batteryItemStack.isEmpty) {
val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve() val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
@ -413,8 +360,12 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
return battery return battery
} }
set(value) {
maxBattery = value
}
override val maxBatteryLevel: ImpreciseFraction get() { override var maxBatteryLevel: ImpreciseFraction
get() {
if (batteryItemStack != ItemStack.EMPTY) { if (batteryItemStack != ItemStack.EMPTY) {
val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve() val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
@ -431,6 +382,9 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
return maxBattery return maxBattery
} }
set(value) {
maxBattery = value
}
private val resolver = LazyOptional.of { this } private val resolver = LazyOptional.of { this }
@ -485,3 +439,5 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
} }
} }
} }
val ICapabilityProvider.android get() = getCapability(MatteryCapability.ANDROID).orNull()

View File

@ -9,45 +9,43 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraftforge.event.AttachCapabilitiesEvent import net.minecraftforge.event.AttachCapabilitiesEvent
import net.minecraftforge.event.entity.player.PlayerEvent.Clone import net.minecraftforge.event.entity.player.PlayerEvent.Clone
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent
import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.android.AndroidResearch import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.AndroidFeatures import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import java.util.* import java.util.*
class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply) { class AndroidCapabilityPlayer(val ply: Player) : AndroidCapability(ply) {
@JvmField override var isAndroid = false
var isAndroid = false private var remoteIsAndroid = false
private var isAndroidNetwork = false
@JvmField
var willBecomeAndroid = false var willBecomeAndroid = false
private var willBecomeAndroidNetwork = false private var willBecomeAndroidNetwork = false
private var shouldPlaySound = false private var shouldPlaySound = false
private val research = ArrayList<AndroidResearch>() private val research = ArrayList<AndroidResearch>()
override fun invalidateNetworkState() { private var remoteBattery = ImpreciseFraction(-1)
super.invalidateNetworkState() private var remoteMaxBattery = ImpreciseFraction(-1)
private var remoteBatteryStack = ItemStack.EMPTY
isAndroidNetwork = false private var invalidateNetworkIn = 10
willBecomeAndroidNetwork = false
for (instance in research) { fun invalidateNetworkState() {
instance.isDirty = true invalidateNetworkIn = 10
}
} }
override fun isAndroid(): Boolean = isAndroid
fun isEverAndroid(): Boolean = isAndroid || willBecomeAndroid fun isEverAndroid(): Boolean = isAndroid || willBecomeAndroid
fun becomeAndroidSoft() { fun becomeAndroidSoft() {
@ -162,41 +160,91 @@ class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply
return instance return instance
} }
@JvmField var lastJumpTicks = 14
var last_jump_ticks = 14
override fun tickNetwork() { override fun tick() {
super.tickNetwork() super.tick()
if (isAndroid != isAndroidNetwork) {
isAndroidNetwork = isAndroid if (invalidateNetworkIn > 0 && --invalidateNetworkIn == 0) {
sendNetwork(AndroidStatusPacket(AndroidStatusPacket.Type.IS_ANDROID, isAndroid, shouldPlaySound)) remoteBattery = ImpreciseFraction.MINUS_ONE
remoteMaxBattery = ImpreciseFraction.MINUS_ONE
remoteBatteryStack = ItemStack.EMPTY
remoteIsAndroid = false
willBecomeAndroidNetwork = false
for (instance in research) {
instance.isDirty = true
instance.invalidateNetwork()
}
for (feature in features.values) {
feature.isDirty = true
feature.invalidateNetwork()
}
}
tickedOnce = true
if (battery != remoteBattery) {
remoteBattery = battery
sendNetwork(AndroidEnergyLevelPacket(level = battery, isMaxEnergy = false))
}
if (maxBattery != remoteMaxBattery) {
remoteMaxBattery = maxBattery
sendNetwork(AndroidEnergyLevelPacket(level = maxBattery, isMaxEnergy = true))
}
if (!remoteBatteryStack.equals(batteryItemStack, false)) {
remoteBatteryStack = batteryItemStack.copy()
sendNetwork(AndroidBatteryItemPacket(batteryItemStack))
}
if (networkQueue.size != 0) {
for (packet in networkQueue) {
AndroidNetworkChannel.send(ply, packet)
}
networkQueue.clear()
}
if (isAndroid != remoteIsAndroid) {
remoteIsAndroid = isAndroid
sendNetwork(IsAndroidPacket(isAndroid))
shouldPlaySound = false shouldPlaySound = false
} }
if (willBecomeAndroid != willBecomeAndroidNetwork) { if (willBecomeAndroid != willBecomeAndroidNetwork) {
willBecomeAndroidNetwork = willBecomeAndroid willBecomeAndroidNetwork = willBecomeAndroid
sendNetwork(AndroidStatusPacket(AndroidStatusPacket.Type.WILL_BECOME_ANDROID, willBecomeAndroid, false)) sendNetwork(WillBecomeAndroidPacket(willBecomeAndroid))
} }
for (instance in research) { for (instance in research) {
if (instance.isDirty) { if (instance.isDirty) {
instance.isDirty = false instance.isDirty = false
sendNetwork(AndroidResearchPacket(instance)) sendNetwork(AndroidResearchSyncPacket(instance))
}
}
for (instance in features.values) {
if (instance.isDirty) {
instance.isDirty = false
sendNetwork(AndroidFeatureSyncPacket(instance))
} }
} }
} }
@JvmField var sleepTicks = 0
var sleep_ticks = 0
override fun tickInnerClientAlways() { override fun tickInnerClientAlways() {
super.tickInnerClientAlways() super.tickInnerClientAlways()
if (willBecomeAndroid) { if (willBecomeAndroid) {
if (ply.isSleeping) { if (ply.isSleeping) {
sleep_ticks++ sleepTicks++
} else { } else {
sleep_ticks = 0 sleepTicks = 0
} }
} }
} }
@ -206,17 +254,17 @@ class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply
if (willBecomeAndroid) { if (willBecomeAndroid) {
if (ply.isSleeping) { if (ply.isSleeping) {
sleep_ticks++ sleepTicks++
if (sleep_ticks > SLEEP_TICKS_LIMIT) { if (sleepTicks > SLEEP_TICKS_LIMIT) {
becomeAndroid() becomeAndroid()
shouldPlaySound = true shouldPlaySound = true
sleep_ticks = 0 sleepTicks = 0
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false) (ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
} }
} else { } else {
sleep_ticks = 0 sleepTicks = 0
} }
} }
} }
@ -271,12 +319,13 @@ class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply
@SubscribeEvent @SubscribeEvent
fun onPlayerChangeDimensionEvent(event: PlayerChangedDimensionEvent) { fun onPlayerChangeDimensionEvent(event: PlayerChangedDimensionEvent) {
event.player.getCapability(MatteryCapability.ANDROID) event.player.getCapability(MatteryCapability.ANDROID)
.ifPresent { it.invalidateNetworkState() } .ifPresentK { (it as AndroidCapabilityPlayer).invalidateNetworkState() }
} }
@SubscribeEvent @SubscribeEvent
fun onPlayerCloneEvent(event: Clone) { fun onPlayerCloneEvent(event: Clone) {
event.player.getCapability(MatteryCapability.ANDROID).ifPresent { val it = event.player.android as AndroidCapabilityPlayer? ?: return
var resolver = event.original.getCapability(MatteryCapability.ANDROID) var resolver = event.original.getCapability(MatteryCapability.ANDROID)
if (!resolver.isPresent || resolver.resolve().isEmpty) { if (!resolver.isPresent || resolver.resolve().isEmpty) {
@ -286,7 +335,7 @@ class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply
if (!resolver.isPresent || resolver.resolve().isEmpty) { if (!resolver.isPresent || resolver.resolve().isEmpty) {
event.original.invalidateCaps() event.original.invalidateCaps()
return@ifPresent return
} }
val original = resolver.resolve().get() as AndroidCapabilityPlayer val original = resolver.resolve().get() as AndroidCapabilityPlayer
@ -300,7 +349,6 @@ class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply
it.invalidateNetworkState() it.invalidateNetworkState()
event.original.invalidateCaps() event.original.invalidateCaps()
} }
}
val ENERGY_FOR_HUNGER_POINT = ImpreciseFraction(1000) val ENERGY_FOR_HUNGER_POINT = ImpreciseFraction(1000)
const val SLEEP_TICKS_LIMIT = 80 const val SLEEP_TICKS_LIMIT = 80

View File

@ -22,6 +22,10 @@ interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundT
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T?
fun <T : AndroidFeature> computeIfAbsent(feature: AndroidFeatureType<T>): T {
return getFeature(feature) ?: addFeature(feature)
}
fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> { fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
val get = getFeature(feature) val get = getFeature(feature)
return if (get != null) Optional.of(get) else Optional.empty() return if (get != null) Optional.of(get) else Optional.empty()
@ -35,11 +39,11 @@ interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundT
} }
} }
fun isAndroid(): Boolean = true val isAndroid: Boolean get() = true
fun onHurt(event: LivingHurtEvent) {} fun onHurt(event: LivingHurtEvent) {}
var batteryItemStack: ItemStack var batteryItemStack: ItemStack
fun invalidateNetworkState() // tell capability that player forgot everything, and everything needs to be re-networked
fun setEnergy(value: ImpreciseFraction) override var batteryLevel: ImpreciseFraction
fun setMaxEnergy(value: ImpreciseFraction) override var maxBatteryLevel: ImpreciseFraction
} }

View File

@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.block.entity.GravitationStabilizerBlockEntity import ru.dbotthepony.mc.otm.block.entity.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android.android
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MItems
import kotlin.math.PI import kotlin.math.PI
@ -144,10 +145,8 @@ class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context
if (!Minecraft.getInstance().options.hideGui && ( if (!Minecraft.getInstance().options.hideGui && (
(ply.mainHandItem.item == MItems.BLACK_HOLE_SCANNER || ply.offhandItem.item == MItems.BLACK_HOLE_SCANNER) && (ply.mainHandItem.item == MItems.BLACK_HOLE_SCANNER || ply.offhandItem.item == MItems.BLACK_HOLE_SCANNER) &&
poseStack.translation().length() < tile.gravitationStrength * 64.0 || poseStack.translation().length() < tile.gravitationStrength * 64.0 ||
(ply.abilities.instabuild || ply.abilities.invulnerable || (ply.abilities.instabuild || ply.android?.isAndroid == true) &&
ply.getCapability(MatteryCapability.ANDROID).isPresent && poseStack.translation().length() < tile.gravitationStrength * 28.0
ply.getCapability(MatteryCapability.ANDROID).resolve().get().isAndroid()) &&
poseStack.translation().length() < tile.gravitationStrength * 32.0
)) { )) {
val facing = tile.blockPos.asVector() - Minecraft.getInstance().gameRenderer.mainCamera.position val facing = tile.blockPos.asVector() - Minecraft.getInstance().gameRenderer.mainCamera.position
val distance = facing.length() val distance = facing.length()

View File

@ -19,8 +19,9 @@ import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.core.RGBAColor import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import ru.dbotthepony.mc.otm.network.AndroidNetworkChannel
import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket
import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.MatteryNetworking
import ru.dbotthepony.mc.otm.network.android.AndroidResearchRequestPacket
import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.* import java.util.*
@ -73,7 +74,7 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
override fun mouseClickedInner(mouse_x: Double, mouse_y: Double, mouse_click_type: Int): Boolean { override fun mouseClickedInner(mouse_x: Double, mouse_y: Double, mouse_click_type: Int): Boolean {
if (mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT) { if (mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT) {
if (node.canResearch && !node.isResearched) { if (node.canResearch && !node.isResearched) {
MatteryNetworking.CHANNEL.sendToServer(AndroidResearchRequestPacket(node.type)) AndroidNetworkChannel.sendToServer(AndroidResearchRequestPacket(node.type))
} }
ru.dbotthepony.mc.otm.client.minecraft.soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0f)) ru.dbotthepony.mc.otm.client.minecraft.soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0f))

View File

@ -41,7 +41,7 @@ class HealPillItem : Item(Properties().stacksTo(64).rarity(Rarity.UNCOMMON).tab(
override fun use(level: Level, ply: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> { override fun use(level: Level, ply: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
val resolver = ply.getCapability(MatteryCapability.ANDROID).resolve() val resolver = ply.getCapability(MatteryCapability.ANDROID).resolve()
if (!resolver.isEmpty && resolver.get().isAndroid()) if (!resolver.isEmpty && resolver.get().isAndroid)
return super.use(level, ply, hand) return super.use(level, ply, hand)
ply.startUsingItem(hand) ply.startUsingItem(hand)
@ -51,7 +51,7 @@ class HealPillItem : Item(Properties().stacksTo(64).rarity(Rarity.UNCOMMON).tab(
override fun finishUsingItem(stack: ItemStack, level: Level, ent: LivingEntity): ItemStack { override fun finishUsingItem(stack: ItemStack, level: Level, ent: LivingEntity): ItemStack {
val resolver = ent.getCapability(MatteryCapability.ANDROID).resolve() val resolver = ent.getCapability(MatteryCapability.ANDROID).resolve()
if (!resolver.isEmpty && resolver.get().isAndroid()) if (!resolver.isEmpty && resolver.get().isAndroid)
return super.finishUsingItem(stack, level, ent) return super.finishUsingItem(stack, level, ent)
stack.shrink(1) stack.shrink(1)
@ -106,7 +106,7 @@ class PillItem(val pillType: PillType) :
if ( if (
pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid() || pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid() ||
pillType == PillType.BECOME_HUMANE && cap.isEverAndroid() || pillType == PillType.BECOME_HUMANE && cap.isEverAndroid() ||
pillType == PillType.OBLIVION && cap.isAndroid() pillType == PillType.OBLIVION && cap.isAndroid
) { ) {
ply.startUsingItem(hand) ply.startUsingItem(hand)
return InteractionResultHolder.consume(ply.getItemInHand(hand)) return InteractionResultHolder.consume(ply.getItemInHand(hand))
@ -127,7 +127,7 @@ class PillItem(val pillType: PillType) :
val cap = resolver.get() as AndroidCapabilityPlayer val cap = resolver.get() as AndroidCapabilityPlayer
if (pillType == PillType.OBLIVION && cap.isAndroid()) { if (pillType == PillType.OBLIVION && cap.isAndroid) {
if (!ent.abilities.instabuild) { if (!ent.abilities.instabuild) {
stack.shrink(1) stack.shrink(1)
} }

View File

@ -0,0 +1,241 @@
package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.ItemStack
import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT
import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.android.*
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.readImpreciseFraction
import ru.dbotthepony.mc.otm.core.writeImpreciseFraction
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.io.ByteArrayInputStream
import java.io.OutputStream
import java.util.function.Supplier
sealed class AndroidStatusPacket : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {}
protected abstract fun play(capability: AndroidCapabilityPlayer)
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
play(minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork)
}
}
}
object IsAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
capability.isAndroid = true
}
}
object IsNotAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
capability.isAndroid = false
}
}
fun IsAndroidPacket(status: Boolean) = if (status) IsAndroidPacket else IsNotAndroidPacket
object WillBecomeAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
capability.willBecomeAndroid = true
}
}
object WillNotBecomeAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
capability.willBecomeAndroid = false
}
}
fun WillBecomeAndroidPacket(status: Boolean) = if (status) WillBecomeAndroidPacket else WillNotBecomeAndroidPacket
class AndroidResearchRequestPacket(val type: AndroidResearchType<*>) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_RESEARCH.getID(type))
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val ply = context.get().sender ?: return@enqueueWork
val android = ply.android as? AndroidCapabilityPlayer ?: return@enqueueWork
if (!android.isAndroid || ply.containerMenu !is AndroidStationMenu)
return@enqueueWork
android.getResearch(type).research()
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidResearchRequestPacket {
return AndroidResearchRequestPacket(MRegistry.ANDROID_RESEARCH.getValue(buff.readInt()))
}
}
}
private fun getData(invoke: (stream: OutputStream) -> Unit): FastByteArrayOutputStream {
val stream = FastByteArrayOutputStream()
invoke(stream)
return stream
}
class AndroidResearchSyncPacket(val type: AndroidResearchType<*>, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : MatteryPacket {
constructor(feature: AndroidResearch) : this(feature.type, getData(feature::writeNetwork), null)
override fun write(buff: FriendlyByteBuf) {
dataList ?: throw NullPointerException("No byte list is present")
buff.writeInt(MRegistry.ANDROID_RESEARCH.getID(type))
buff.writeBytes(dataList.array, 0, dataList.length)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
dataBytes ?: throw NullPointerException("No data bytes array is present")
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
android.getResearch(type).readNetwork(ByteArrayInputStream(dataBytes))
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidResearchSyncPacket {
return AndroidResearchSyncPacket(
MRegistry.ANDROID_RESEARCH.getValue(buff.readInt()),
null, ByteArray(buff.readableBytes()).also { buff.readBytes(it) }
)
}
}
}
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : MatteryPacket {
constructor(feature: AndroidFeature) : this(feature.type, getData(feature::writeNetwork), null)
override fun write(buff: FriendlyByteBuf) {
dataList ?: throw NullPointerException("No byte list is present")
buff.writeInt(MRegistry.ANDROID_FEATURES.getID(type))
buff.writeBytes(dataList.array, 0, dataList.length)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
dataBytes ?: throw NullPointerException("No data bytes array is present")
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
android.computeIfAbsent(type).readNetwork(ByteArrayInputStream(dataBytes))
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidFeatureSyncPacket {
return AndroidFeatureSyncPacket(
MRegistry.ANDROID_FEATURES.getValue(buff.readInt()),
null, ByteArray(buff.readableBytes()).also { buff.readBytes(it) }
)
}
}
}
class AndroidFeatureRemovePacket(val type: AndroidFeatureType<*>) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getID(type))
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
android.removeFeature(type)
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidFeatureRemovePacket {
return AndroidFeatureRemovePacket(MRegistry.ANDROID_FEATURES.getValue(buff.readInt()))
}
}
}
class AndroidBatteryItemPacket(val item: ItemStack) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeItem(item)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
android.batteryItemStack = item
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidBatteryItemPacket {
return AndroidBatteryItemPacket(buff.readItem())
}
}
}
class AndroidEnergyLevelPacket(val level: ImpreciseFraction, val isMaxEnergy: Boolean) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(isMaxEnergy)
buff.writeImpreciseFraction(level)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
if (isMaxEnergy) {
android.maxBatteryLevel = level
} else {
android.batteryLevel = level
}
}
}
companion object {
fun read(buff: FriendlyByteBuf): AndroidEnergyLevelPacket {
return AndroidEnergyLevelPacket(buff.readImpreciseFraction(), buff.readBoolean())
}
}
}
object AndroidNetworkChannel : MatteryNetworkChannel(
version = "1",
name = "android"
) {
fun register() {
add(IsAndroidPacket::class.java, { IsAndroidPacket }, PLAY_TO_CLIENT)
add(IsNotAndroidPacket::class.java, { IsNotAndroidPacket }, PLAY_TO_CLIENT)
add(WillBecomeAndroidPacket::class.java, { WillBecomeAndroidPacket }, PLAY_TO_CLIENT)
add(WillNotBecomeAndroidPacket::class.java, { WillNotBecomeAndroidPacket }, PLAY_TO_CLIENT)
add(AndroidResearchRequestPacket::class.java, AndroidResearchRequestPacket.Companion::read, PLAY_TO_SERVER)
add(AndroidResearchSyncPacket::class.java, AndroidResearchSyncPacket.Companion::read, PLAY_TO_CLIENT)
add(AndroidFeatureSyncPacket::class.java, AndroidFeatureSyncPacket.Companion::read, PLAY_TO_CLIENT)
add(AndroidFeatureRemovePacket::class.java, AndroidFeatureRemovePacket.Companion::read, PLAY_TO_CLIENT)
add(AndroidBatteryItemPacket::class.java, AndroidBatteryItemPacket.Companion::read, PLAY_TO_CLIENT)
add(AndroidEnergyLevelPacket::class.java, AndroidEnergyLevelPacket.Companion::read, PLAY_TO_CLIENT)
}
}

View File

@ -0,0 +1,61 @@
package ru.dbotthepony.mc.otm.network
import java.util.function.Function
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
import net.minecraftforge.network.NetworkDirection
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.NetworkRegistry
import net.minecraftforge.network.PacketDistributor
import net.minecraftforge.network.simple.SimpleChannel
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import java.util.Optional
import java.util.function.BiConsumer
import java.util.function.Supplier
interface MatteryPacket {
fun write(buff: FriendlyByteBuf)
fun play(context: Supplier<NetworkEvent.Context>)
}
abstract class MatteryNetworkChannel(val version: String, val name: String) {
val channel: SimpleChannel = NetworkRegistry.newSimpleChannel(
ResourceLocation(OverdriveThatMatters.MOD_ID, name),
{ version },
{ it == version },
{ it == version },
)
fun sendToServer(packet: Any) = channel.sendToServer(packet)
fun send(ply: Player, packet: Any) {
if (ply is ServerPlayer) {
channel.send(PacketDistributor.PLAYER.with { ply }, packet)
}
}
fun send(distributor: PacketDistributor.PacketTarget, packet: Any) = channel.send(distributor, packet)
private var nextNetworkPacketID = 0
fun <T> add(
packetClass: Class<T>,
writer: BiConsumer<T, FriendlyByteBuf>,
reader: Function<FriendlyByteBuf, T>,
handler: BiConsumer<T, Supplier<NetworkEvent.Context>>,
direction: NetworkDirection? = null
) {
@Suppress("INACCESSIBLE_TYPE")
channel.registerMessage(nextNetworkPacketID++, packetClass, writer, reader, handler, if (direction == null) Optional.empty() else Optional.of(direction))
}
fun <T : MatteryPacket> add(
packetClass: Class<T>,
reader: Function<FriendlyByteBuf, T>,
direction: NetworkDirection? = null
) {
add(packetClass, MatteryPacket::write, reader, MatteryPacket::play, direction)
}
}