SubwayPlugin.java v1.1
地铁插件,OP上线自动发放三种工具,支持站台、闸机创建、乘车及经济系统
命令列表
- meony充值金币/矿物兑换
package cn.railcraft.subway;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @pluginName SubwayPlugin
* @author CouerDev
* @version 1.1
* @description 地铁插件,OP上线自动发放三种工具,支持站台、闸机创建、乘车及经济系统
* [command]meony|充值金币/矿物兑换[/command]
*/
public class SubwayPlugin extends JavaPlugin implements Listener, TabExecutor {
// 道具名称
private static final String STATION_CREATOR = "§e站台创建器";
private static final String ENTRANCE_CREATOR = "§b入站闸机创建器";
private static final String EXIT_CREATOR = "§d出站闸机创建器";
// 数据文件
private File dataFile;
private Map<String, Station> stations = new LinkedHashMap<>();
private Set<String> entranceGates = new HashSet<>();
private Set<String> exitGates = new HashSet<>();
private Map<UUID, Integer> balances = new ConcurrentHashMap<>();
private Map<UUID, String> ridingStation = new HashMap<>();
// 兑换比率
private static final Map<Material, Integer> MATERIAL_COINS = new HashMap<>();
static {
MATERIAL_COINS.put(Material.IRON_INGOT, 2);
MATERIAL_COINS.put(Material.GOLD_INGOT, 10);
MATERIAL_COINS.put(Material.DIAMOND, 50);
}
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, this);
getCommand("meony").setExecutor(this);
dataFile = new File(getDataFolder(), "subway-data.yml");
loadData();
getLogger().info("SubwayPlugin 已启用");
}
@Override
public void onDisable() {
saveData();
getLogger().info("SubwayPlugin 已关闭");
}
// 玩家加入事件:自动OP发放工具
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player p = event.getPlayer();
if (p.isOp()) {
giveOpCreatorItems(p);
}
}
// 发放三种工具
private void giveOpCreatorItems(Player p) {
p.getInventory().addItem(getCreatorItem("station"));
p.getInventory().addItem(getCreatorItem("entrance"));
p.getInventory().addItem(getCreatorItem("exit"));
p.sendMessage("§a已自动发放地铁工具:站台创建器、入站闸机创建器、出站闸机创建器!");
}
// 站台结构
private static class Station {
String name;
Location railLoc;
Station(String name, Location loc) { this.name = name; this.railLoc = loc; }
Map<String, Object> serialize() {
Map<String, Object> m = new LinkedHashMap<>();
m.put("name", name);
m.put("world", railLoc.getWorld().getName());
m.put("x", railLoc.getX());
m.put("y", railLoc.getY());
m.put("z", railLoc.getZ());
return m;
}
static Station deserialize(Map<String,Object> m) {
return new Station(
(String)m.get("name"),
new Location(Bukkit.getWorld((String)m.get("world")),
(double)m.get("x"),(double)m.get("y"),(double)m.get("z"))
);
}
}
// 物品生成
public ItemStack getCreatorItem(String type){
ItemStack stick = new ItemStack(Material.STICK);
ItemMeta meta = stick.getItemMeta();
switch (type) {
case "station": meta.setDisplayName(STATION_CREATOR); break;
case "entrance": meta.setDisplayName(ENTRANCE_CREATOR); break;
case "exit": meta.setDisplayName(EXIT_CREATOR); break;
}
stick.setItemMeta(meta);
return stick;
}
// 事件监听,包含:道具右键,玩家上车
@EventHandler
public void onPlayerInteract(PlayerInteractEvent e){
Player p = e.getPlayer();
if (e.getItem() == null || !e.getAction().name().startsWith("RIGHT")) return;
ItemStack item = e.getItem();
// 1. 站台创建器右键铁轨
if (item.getType() == Material.STICK && STATION_CREATOR.equals(item.getItemMeta().getDisplayName())) {
if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.RAIL) {
e.setCancelled(true);
p.sendMessage("§e请输入站台名称(聊天框),输入后自动创建!");
getServer().getScheduler().runTaskLater(this, () -> {
StationChatListener.waiting.put(p.getUniqueId(), e.getClickedBlock().getLocation());
}, 2L);
}
}
// 2. 入闸机创建器 右键木栅栏
else if (item.getType() == Material.STICK && ENTRANCE_CREATOR.equals(item.getItemMeta().getDisplayName())) {
if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.OAK_FENCE) {
e.setCancelled(true);
String locStr = locToStr(e.getClickedBlock().getLocation());
entranceGates.add(locStr);
p.sendMessage("§a已设置为入站闸机点");
saveData();
}
}
// 3. 出闸机创建器 右键木栅栏
else if (item.getType() == Material.STICK && EXIT_CREATOR.equals(item.getItemMeta().getDisplayName())) {
if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.OAK_FENCE) {
e.setCancelled(true);
String locStr = locToStr(e.getClickedBlock().getLocation());
exitGates.add(locStr);
p.sendMessage("§d已设置为出站闸机点");
saveData();
}
}
// 4. 玩家右键铁轨上车
else if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.RAIL) {
String start = getStationByRail(e.getClickedBlock().getLocation());
if (start == null) return;
if (!ridingStation.containsKey(p.getUniqueId())) {
p.sendMessage("§c请先通过入闸机进入站台!");
return;
}
startRide(p, start);
e.setCancelled(true);
}
}
// 监听聊天,站台命名流程
public static class StationChatListener implements Listener {
static Map<UUID, Location> waiting = new HashMap<>();
private SubwayPlugin plugin;
public StationChatListener(SubwayPlugin plugin) { this.plugin = plugin; }
@EventHandler
public void onPlayerChat(org.bukkit.event.player.AsyncPlayerChatEvent e) {
Player p = e.getPlayer();
if (waiting.containsKey(p.getUniqueId())) {
e.setCancelled(true);
Location loc = waiting.remove(p.getUniqueId());
String name = e.getMessage().trim();
if (name.length()<1 || plugin.stations.containsKey(name)) {
p.sendMessage("§c站台名非法或重复,请重试!");
return;
}
plugin.stations.put(name, new Station(name, loc));
p.sendMessage("§e地铁站已创建: " + name);
plugin.saveData();
}
}
}
// 地铁乘车逻辑
private void startRide(Player p, String startStation) {
List<String> stationNames = new ArrayList<>(stations.keySet());
int idx = stationNames.indexOf(startStation);
if (idx == -1) {
p.sendMessage("§c站点未登记!");
return;
}
ridingStation.remove(p.getUniqueId());
p.sendMessage("§a已乘上地铁,始发站: " + startStation);
new BukkitRunnable() {
int pos = idx;
@Override
public void run() {
if (pos >= stationNames.size()) { p.sendMessage("§b已到终点站,旅途结束!"); this.cancel(); return; }
String now = stationNames.get(pos);
Station st = stations.get(now);
if (balances.getOrDefault(p.getUniqueId(),0) < 1) {
p.sendMessage("§c金币不足,无法继续乘坐!");
this.cancel(); return;
}
balances.put(p.getUniqueId(), balances.get(p.getUniqueId())-1);
p.teleport(st.railLoc);
p.sendMessage("§6已到达站台: " + now + ",扣费1圆,余额: " + balances.get(p.getUniqueId()));
pos++;
}
}.runTaskTimer(this, 0L, 80L); //80L=4秒
}
// 查找站台名
private String getStationByRail(Location loc){
for(Station st:stations.values()){
if(st.railLoc.getBlock().equals(loc.getBlock())) return st.name;
}
return null;
}
// 闸机进入流程
@EventHandler
public void onEnterGate(PlayerInteractEvent e){
if(e.getItem() != null) return;
if(e.getClickedBlock()!=null && e.getAction().name().startsWith("RIGHT") && e.getClickedBlock().getType()==Material.OAK_FENCE){
Player p = e.getPlayer();
String locStr = locToStr(e.getClickedBlock().getLocation());
if(entranceGates.contains(locStr)){
e.setCancelled(true);
ridingStation.put(p.getUniqueId(), "");
p.sendMessage("§b已通过入站闸机,可以乘坐地铁!");
}
else if(exitGates.contains(locStr)){
e.setCancelled(true);
ridingStation.remove(p.getUniqueId());
p.sendMessage("§d已通过出站闸机,乘车流程结束!");
}
}
}
// 经济命令 /meony 参数为空则兑换矿物锭,有数字则直接充值
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if(!(sender instanceof Player))return false;
Player p = (Player)sender;
if(args.length==1){
try{
int val=Integer.parseInt(args[0]);
if (val<=0) return false;
balances.put(p.getUniqueId(),balances.getOrDefault(p.getUniqueId(),0)+val);
p.sendMessage("§e已充值金币: "+val+" 当前余额:"+balances.get(p.getUniqueId()));
saveData();
return true;
}catch(Exception ex){
p.sendMessage("§c参数错误,正确格式:/meony <金额>");
}
}else{
int coins=0;
for(Map.Entry<Material,Integer> entry:MATERIAL_COINS.entrySet()){
int amt=countItem(p,entry.getKey());
if(amt>0){
removeItem(p,entry.getKey(),amt);
coins+=amt*entry.getValue();
p.sendMessage("§a已兑换 " + amt + entry.getKey().name()+", 获取金币:"+ (amt*entry.getValue()));
}
}
if(coins>0){
balances.put(p.getUniqueId(),balances.getOrDefault(p.getUniqueId(),0)+coins);
p.sendMessage("§e矿物兑换完毕!当前余额: "+balances.get(p.getUniqueId()));
saveData();
}else{
p.sendMessage("§c未检测到可兑换矿物锭: 铁锭2圆, 金锭10圆, 钻石50圆");
}
}
return true;
}
@Override
public List<String> onTabComplete(CommandSender s, Command c, String l, String[] args) { return Collections.emptyList(); }
// ------------ 辅助方法 -------------
private int countItem(Player p,Material m){
int count=0;
for(ItemStack i:p.getInventory().getContents()){
if(i!=null&&i.getType()==m)count+=i.getAmount();
}
return count;
}
private void removeItem(Player p,Material m,int amt){
for(ItemStack i:p.getInventory().getContents()){
if(i!=null&&i.getType()==m){
int a=i.getAmount();
if(amt>=a){
i.setAmount(0);amt-=a;
}else{
i.setAmount(a-amt);amt=0;
}
if(amt<=0)break;
}
}
}
private String locToStr(Location loc){
return loc.getWorld().getName()+":"+loc.getBlockX()+","+loc.getBlockY()+","+loc.getBlockZ();
}
// ------------ 数据存储与加载 ------------
private void saveData(){
Yaml yaml = new Yaml();
Map<String, Object> map = new LinkedHashMap<>();
List<Map<String,Object>> stationList = new ArrayList<>();
for(Station st:stations.values()){
stationList.add(st.serialize());
}
map.put("stations",stationList);
map.put("entranceGates",entranceGates);
map.put("exitGates",exitGates);
Map<String,Integer> bal = new HashMap<>();
for(UUID k:balances.keySet()){
bal.put(k.toString(),balances.get(k));
}
map.put("balances",bal);
try(FileWriter fw = new FileWriter(dataFile)){
yaml.dump(map,fw);
}catch(IOException ex){ex.printStackTrace();}
}
private void loadData(){
stations.clear();entranceGates.clear();exitGates.clear();balances.clear();
if(!dataFile.exists())return;
Yaml yaml = new Yaml();
try(FileInputStream fr = new FileInputStream(dataFile)){
Map<String,Object> m = yaml.load(fr);
if(m==null)return;
List<Map<String,Object>> stationList = (List<Map<String,Object>>)m.getOrDefault("stations",new ArrayList<>());
for(Map<String,Object> ent:stationList){
Station st = Station.deserialize(ent);
stations.put(st.name,st);
}
entranceGates.addAll((List<String>)m.getOrDefault("entranceGates",new ArrayList<>()));
exitGates.addAll((List<String>)m.getOrDefault("exitGates",new ArrayList<>()));
Map<String,Integer> bal = (Map<String,Integer>)m.getOrDefault("balances",new HashMap<>());
for(String s:bal.keySet())balances.put(UUID.fromString(s),bal.get(s));
}catch(Exception ex){ex.printStackTrace();}
Bukkit.getPluginManager().registerEvents(new StationChatListener(this), this);
}
}