This commit is contained in:
parent
dde8700248
commit
916f44da47
7 changed files with 231 additions and 52 deletions
|
@ -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
|
||||
|
||||
|
|
24
retstone.py
24
retstone.py
|
@ -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)
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
76
src/main/java/eu/m724/tweaks/redstone/Store.java
Normal file
76
src/main/java/eu/m724/tweaks/redstone/Store.java
Normal 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());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue