redstone WIP 2
All checks were successful
/ build (push) Successful in 1m1s

This commit is contained in:
Minecon724 2024-12-30 21:07:47 +01:00
parent dde8700248
commit 916f44da47
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
7 changed files with 231 additions and 52 deletions

View file

@ -19,10 +19,10 @@ action bits 2-28 of repeater id
- payload: power level if writing
If writing, no response \
If reading, response is the power level, or -1 if no repeater with that id (subject to change)
If reading, response is the power level, or 0 if not powered or no repeater with that ID
Reading powers the block down of course \
If the block was powered, you should power it down (or read), wait some, and then read again
BUT you should power it down (or read), wait some, and then read again
### Retstone

View file

@ -22,11 +22,23 @@ def set_power(repeater_id: int, power: int):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(message.to_bytes(4), ENDPOINT)
repeater_id = 235459920
print("Reading from repeater")
power = get_power(repeater_id)
print("Read power:", power)
if __name__ == "__main__":
from argparse import ArgumentParser
print("Powering repeater with power 10")
set_power(repeater_id, 10)
parser = ArgumentParser()
parser.add_argument("repeater_id", help="The repeater ID", type=int)
parser.add_argument("-p", "--power", help="Power the repeater with specified power. Leave to read.", type=int)
parser.add_argument("-c", "--copy", help="Copy input of one repeater to another. If combined with power, power is added.", type=int)
args = parser.parse_args()
if args.copy is None:
if args.power is None:
power = get_power(args.repeater_id)
print(power)
else:
set_power(args.repeater_id, args.power)
else:
while True:
power = get_power(args.repeater_id)
set_power(args.copy, power)

View file

@ -51,4 +51,17 @@ public class RedstoneListener implements Listener {
event.getPlayer().sendMessage("Repeater ID: " + id);
}
@EventHandler
public void b(BlockRedstoneEvent event) {
var block = event.getBlock();
System.out.println(block);
var id = redstoneRepeaters.getId(block);
if (id == Integer.MIN_VALUE) return;
System.out.println("yes it isi");
event.setNewCurrent(redstoneRepeaters.getOutboundPower(id));
}
}

View file

@ -28,6 +28,8 @@ public class RedstoneManager {
}
public void init(PluginCommand command) {
Store.init(plugin);
plugin.getServer().getPluginManager().registerEvents(new RedstoneListener(redstoneRepeaters), plugin);
command.setExecutor(new RedstoneCommands(redstoneRepeaters));
@ -66,17 +68,17 @@ public class RedstoneManager {
}
boolean write = (buf[0] >> 7 & 1) == 1;
byte state = (byte) (buf[3] & 0xF);
byte data = (byte) (buf[3] & 0xF);
int repeaterId = ((buf[0] & 0x7F) << 24) | ((buf[1] & 0xFF) << 16) | ((buf[2] & 0xFF) << 8) | (buf[3] & 0xF0);
if (write) {
enqueueUpdate(repeaterId, state);
enqueueUpdate(repeaterId, data);
} else {
var newPacket = new DatagramPacket(new byte[1], 1, packet.getSocketAddress());
enqueueRetrieve(repeaterId, value -> {
System.out.println("retieved state " + value);
newPacket.setData(new byte[] { value });
newPacket.setData(new byte[] { (byte) Math.max(value, 0) });
try {
socket.send(newPacket);
@ -89,9 +91,9 @@ public class RedstoneManager {
});
}
private void enqueueUpdate(int repeaterId, byte state) {
System.out.println("Update enqueud " + repeaterId + " " + state);
runnable.enqueueUpdate(repeaterId, state);
private void enqueueUpdate(int repeaterId, byte power) {
System.out.println("Update enqueud " + repeaterId + " " + power);
runnable.enqueueUpdate(repeaterId, power);
}
private void enqueueRetrieve(int repeaterId, Consumer<Byte> consumer) {

View file

@ -6,49 +6,40 @@
package eu.m724.tweaks.redstone;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import eu.m724.tweaks.Language;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.data.AnaloguePowerable;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
public class RedstoneRepeaters {
private final Plugin plugin;
private final NamespacedKey repeaterKey;
private final Map<Integer, Location> repeatersById = new HashMap<>();
private final Map<Location, Integer> repeatersByLocation = new HashMap<>();
private final BiMap<Integer, Location> repeatersById = HashBiMap.create();
private final Map<Integer, Byte> repeatersByPower = new HashMap<>();
public RedstoneRepeaters(Plugin plugin) {
this.plugin = plugin;
this.repeaterKey = new NamespacedKey(plugin, "repeater");
}
//
public boolean isRepeater(ItemStack itemStack) {
var meta = itemStack.getItemMeta();
if (meta == null) return false;
var value = meta.getPersistentDataContainer().get(repeaterKey, PersistentDataType.BOOLEAN);
return value != null;
}
public int getId(Block block) {
return repeatersByLocation.getOrDefault(block.getLocation(), Integer.MIN_VALUE);
}
//
/* Item functions */
public ItemStack give() {
var itemStack = new ItemStack(Material.RED_STAINED_GLASS);
var itemStack = new ItemStack(Material.DAYLIGHT_DETECTOR);
var meta = itemStack.getItemMeta();
meta.setItemName(Language.getString("retstoneBlockItem"));
@ -59,15 +50,23 @@ public class RedstoneRepeaters {
return itemStack;
}
// TODO save in those
public boolean isRepeater(ItemStack itemStack) {
var meta = itemStack.getItemMeta();
if (meta == null) return false;
var value = meta.getPersistentDataContainer().get(repeaterKey, PersistentDataType.BOOLEAN);
return value != null;
}
/* Event */
int onPlace(Block block) {
var repeaterId = ThreadLocalRandom.current().nextInt() & 0x7FFFFFF0;
repeatersById.put(repeaterId, block.getLocation());
repeatersByLocation.put(block.getLocation(), repeaterId);
block.setMetadata("rid", new FixedMetadataValue(plugin, repeaterId));
Store.getInstance().saveRepeaterData(block.getLocation(), repeaterId, (byte) 0);
return repeaterId;
}
@ -75,12 +74,83 @@ public class RedstoneRepeaters {
delete(repeaterId);
}
//
void delete(int repeaterId) {
var location = repeatersById.remove(repeaterId);
if (location == null) return;
repeatersByPower.remove(repeaterId);
Store.getInstance().deleteSavedRepeaterData(location);
}
/* Get functions */
private boolean isValid(int repeaterId) {
// check if there's a repeater with such ID stored
// if not, we're not loading because it's loaded as the block is
var loc = repeatersById.get(repeaterId);
if (loc == null) {
System.err.println("Delete because no loc");
delete(repeaterId);
return false;
}
// check if chunk the block is in is loaded
// you may think it could be simplified, but it can't without loading the chunk (to check if it's loaded)
var isLoaded = loc.getWorld().isChunkLoaded(loc.getBlockX() / 16, loc.getBlockZ() / 16);
if (!isLoaded) return false;
// check if the block is correct type
if (loc.getBlock().getType() != Material.DAYLIGHT_DETECTOR) {
delete(repeaterId);
return false;
}
// check if the block has the same ID bound
var meta = loc.getBlock().getMetadata("rid");
if (meta.isEmpty() || meta.getFirst().asInt() != repeaterId) {
System.err.println("Delete because no meta");
delete(repeaterId);
return false;
}
return true;
}
public int getId(Block block) {
if (block.hasMetadata("rid")) {
var id = block.getMetadata("rid").getFirst().asInt();
return id;
}
var id = repeatersById.inverse().get(block.getLocation());
if (id == null) {
// not in memory, check if repeater
var d = Store.getInstance().getSavedRepeaterData(block.getLocation());
if (d == null) {
block.setMetadata("rid", new FixedMetadataValue(plugin, Integer.MIN_VALUE));
return Integer.MIN_VALUE;
}
id = d.getKey();
block.setMetadata("rid", new FixedMetadataValue(plugin, id));
repeatersById.put(id, block.getLocation());
repeatersByPower.put(id, d.getValue());
}
if (!isValid(id)) return Integer.MIN_VALUE;
return id;
}
Block getBlock(int repeaterId) {
var location = repeatersById.get(repeaterId);
if (location == null) return null;
if (!isValid(repeaterId)) return null;
var storedId = location.getBlock().getMetadata("rid").getFirst().asInt();
if (storedId != repeaterId) {
System.out.println("retrieved but not exitt");
@ -92,35 +162,41 @@ public class RedstoneRepeaters {
return location.getBlock();
}
void delete(int repeaterId) {
var l = repeatersById.remove(repeaterId);
if (l == null) return;
repeatersByLocation.remove(l);
}
/* Control functions */
//
byte getPower(int repeaterId) {
byte getInboundPower(int repeaterId) {
var block = getBlock(repeaterId);
if (block == null) return -1;
block.setType(Material.RED_STAINED_GLASS);
block.getWorld().spawnParticle(Particle.LAVA, block.getLocation().add(0.5, 0.5, 0.5), 3);
System.out.println("Okay I got in power" + block.getBlockPower());
return (byte) block.getBlockPower();
}
byte getOutboundPower(int repeaterId) {
var block = getBlock(repeaterId);
if (block == null) return -1;
System.out.println("Okay I got out power" + repeatersByPower.get(repeaterId));
return repeatersByPower.getOrDefault(repeaterId, (byte) 0);
}
void setPower(int repeaterId, byte power) {
System.out.println(power);
if (power < 0 || power > 15)
throw new IllegalArgumentException("Power should be 0-15, but is " + power);
var block = getBlock(repeaterId);
if (block == null) return;
if (power == 0)
block.setType(Material.RED_STAINED_GLASS);
else
block.setType(Material.REDSTONE_BLOCK);
var data = (AnaloguePowerable) block.getBlockData();
repeatersByPower.put(repeaterId, power);
data.setPower(power);
block.getWorld().spawnParticle(Particle.LAVA, block.getLocation().add(0.5, 0.5, 0.5), 3);
}
System.out.println("Okay I set power");
}
}

View file

@ -39,6 +39,6 @@ public class RedstoneStateUpdateRunnable extends BukkitRunnable {
this.retrieveQueue = new HashMap<>();
updateQueue.forEach((key, value) -> redstoneRepeaters.setPower(key, value));
retrieveQueue.forEach((key, value) -> value.accept(redstoneRepeaters.getPower(key)));
retrieveQueue.forEach((key, value) -> value.accept(redstoneRepeaters.getInboundPower(key)));
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import com.google.common.primitives.Ints;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Location;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class Store {
private static Store INSTANCE;
private final Plugin plugin;
private final File directory;
private Store(Plugin plugin) {
this.plugin = plugin;
this.directory = new File(plugin.getDataFolder(), "storage/redstone");
directory.mkdirs();
}
static void init(Plugin plugin) {
INSTANCE = new Store(plugin);
}
static Store getInstance() {
return INSTANCE;
}
Pair<Integer, Byte> getSavedRepeaterData(Location location) {
var file = getFile(location);
if (!file.exists()) return null;
byte[] bytes;
try {
// TODO read just 4 bytes
bytes = Files.readAllBytes(file.toPath());
} catch (IOException e) {
throw new RuntimeException("Loading saved repeater data", e);
}
var repeaterId = Ints.fromByteArray(bytes) & ~0xF;
var powerLevel = (byte) (bytes[3] & 0xF);
return Pair.of(repeaterId, powerLevel);
}
void saveRepeaterData(Location location, int repeaterId, byte powerLevel) {
var file = getFile(location);
System.out.println(file);
byte[] bytes = Ints.toByteArray((repeaterId & ~0xF) | (powerLevel & 0xF));
try {
Files.write(file.toPath(), bytes);
} catch (IOException e) {
throw new RuntimeException("Saving repeater data", e);
}
}
void deleteSavedRepeaterData(Location location) {
getFile(location).delete();
}
private File getFile(Location location) {
return new File(directory, location.getWorld().getName() + " " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ());
}
}