premier commit

This commit is contained in:
Cortex Builder
2026-06-11 20:14:58 +02:00
commit 16433a893f
33 changed files with 6356 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
[build]
target = "wasm32-wasip2"
+640
View File
@@ -0,0 +1,640 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "bedwars_pumpkin"
version = "1.0.0"
dependencies = [
"pumpkin-plugin-api",
"rand",
"serde",
"serde_yaml",
"tracing",
]
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
dependencies = [
"foldhash",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
"serde",
"serde_core",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "memchr"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
"cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"serde",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pumpkin-plugin-api"
version = "0.1.0"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#30303a8cbd9c3dedac4ff13abd1de64537c6203b"
dependencies = [
"postcard",
"serde_json",
"tracing",
"tracing-serde-structured",
"wit-bindgen",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "tracing-serde-structured"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0832510e9838a4ff7e45e278602ab0533686f9507bc6189e024e488602f29820"
dependencies = [
"hash32",
"heapless",
"serde",
"tracing-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-encoder"
version = "0.247.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b6733b8b91d010a6ac5b0fb237dc46a19650bc4c67db66857e2e787d437204"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.247.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "665fe59e56cc9b419ca6fcca56673e3421d1a5011e3b65caf6b726fd9e041d10"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.247.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80"
dependencies = [
"bitflags",
"hashbrown",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02dee27a2dc20d1008016c742ec9fc6ea498492994ba3750be7454cbc97ff04c"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5007dae772945b7a5003d69d90a3a4a78929d41f19d004e980c4259a6af4484"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af9237d678e3513ad24e96fe98beacdc0db6405284ba2a2400418cf0d42caa89"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.247.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d567162a6b9843080e5e0053f696623ff694bae8ae017c9ec536d1873bbe3d8"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.247.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ffe4064318cdf3c08cb99343b44c039fcefe61ccdf58aa9975285f13d74d1fc"
dependencies = [
"anyhow",
"hashbrown",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "zerocopy"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "bedwars_pumpkin"
version = "1.0.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
pumpkin-plugin-api = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-plugin-api" }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
tracing = "0.1"
rand = "0.8"
+36
View File
@@ -0,0 +1,36 @@
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
pub struct ArenaManager {
pub player_placed_blocks: HashSet<BlockPos>,
}
impl ArenaManager {
pub fn new() -> Self {
Self {
player_placed_blocks: HashSet::new(),
}
}
pub fn track_block(&mut self, pos: BlockPos) {
self.player_placed_blocks.insert(pos);
}
pub fn untrack_block(&mut self, pos: &BlockPos) -> bool {
self.player_placed_blocks.remove(pos)
}
pub fn is_player_placed(&self, pos: &BlockPos) -> bool {
self.player_placed_blocks.contains(pos)
}
pub fn clear(&mut self) {
self.player_placed_blocks.clear();
}
}
+195
View File
@@ -0,0 +1,195 @@
use crate::config::{BedwarsConfig, Loc, TeamConfig, GeneratorConfig};
use crate::team::BedwarsTeam;
use crate::game::GameManager;
pub enum BedwarsCommand {
SetLobby,
SetSpawn { team: BedwarsTeam },
SetBed { team: BedwarsTeam },
SetShop { team: BedwarsTeam },
SetUpgrades { team: BedwarsTeam },
SetGenerator { team: BedwarsTeam },
SetEnderchest { team: BedwarsTeam },
AddGenerator { gen_type: String },
Save,
Start,
Help,
}
impl BedwarsCommand {
pub fn parse(args: &[String]) -> Result<Self, String> {
if args.is_empty() {
return Ok(Self::Help);
}
match args[0].to_lowercase().as_str() {
"setlobby" => Ok(Self::SetLobby),
"setspawn" => {
if args.len() < 2 {
return Err("Usage: /bw setspawn <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetSpawn { team })
}
"setbed" => {
if args.len() < 2 {
return Err("Usage: /bw setbed <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetBed { team })
}
"setshop" => {
if args.len() < 2 {
return Err("Usage: /bw setshop <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetShop { team })
}
"setupgrades" => {
if args.len() < 2 {
return Err("Usage: /bw setupgrades <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetUpgrades { team })
}
"setgenerator" => {
if args.len() < 2 {
return Err("Usage: /bw setgenerator <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetGenerator { team })
}
"setenderchest" => {
if args.len() < 2 {
return Err("Usage: /bw setenderchest <red|blue|purple|yellow>".to_string());
}
let team = BedwarsTeam::from_str(&args[1]).ok_or("Invalid team name")?;
Ok(Self::SetEnderchest { team })
}
"addgenerator" => {
if args.len() < 2 {
return Err("Usage: /bw addgenerator <diamond|emerald>".to_string());
}
let gen_type = args[1].to_lowercase();
if gen_type != "diamond" && gen_type != "emerald" {
return Err("Generator type must be 'diamond' or 'emerald'".to_string());
}
Ok(Self::AddGenerator { gen_type })
}
"save" => Ok(Self::Save),
"start" => Ok(Self::Start),
"help" => Ok(Self::Help),
_ => Err("Unknown command. Type /bw help for options.".to_string()),
}
}
pub fn execute(
&self,
config: &mut BedwarsConfig,
game_manager: &mut GameManager,
player_loc: Loc,
target_block_loc: Option<Loc>,
) -> Result<String, String> {
match self {
Self::SetLobby => {
config.locations.lobby = player_loc;
Ok("Lobby location updated in config".to_string())
}
Self::SetSpawn { team } => {
let team_name = team.get_name().to_lowercase();
let team_conf = config.locations.teams.entry(team_name.clone()).or_insert_with(|| TeamConfig {
enabled: true,
spawn: player_loc.clone(),
bed: player_loc.clone(),
generator: player_loc.clone(),
shop: player_loc.clone(),
upgrades: player_loc.clone(),
enderchest: player_loc.clone(),
});
team_conf.spawn = player_loc;
team_conf.enabled = true;
game_manager.enable_team(*team);
Ok(format!("Spawn for team {} set and enabled", team.get_name()))
}
Self::SetBed { team } => {
let block_loc = target_block_loc.ok_or("You must look at a bed block")?;
let team_name = team.get_name().to_lowercase();
if let Some(t_conf) = config.locations.teams.get_mut(&team_name) {
t_conf.bed = block_loc;
Ok(format!("Bed for team {} set", team.get_name()))
} else {
Err("Team not configured yet. Set spawn first.".to_string())
}
}
Self::SetShop { team } => {
let team_name = team.get_name().to_lowercase();
if let Some(t_conf) = config.locations.teams.get_mut(&team_name) {
t_conf.shop = player_loc;
Ok(format!("Shop for team {} set", team.get_name()))
} else {
Err("Team not configured yet. Set spawn first.".to_string())
}
}
Self::SetUpgrades { team } => {
let team_name = team.get_name().to_lowercase();
if let Some(t_conf) = config.locations.teams.get_mut(&team_name) {
t_conf.upgrades = player_loc;
Ok(format!("Upgrades for team {} set", team.get_name()))
} else {
Err("Team not configured yet. Set spawn first.".to_string())
}
}
Self::SetGenerator { team } => {
let team_name = team.get_name().to_lowercase();
if let Some(t_conf) = config.locations.teams.get_mut(&team_name) {
t_conf.generator = player_loc;
Ok(format!("Generator for team {} set", team.get_name()))
} else {
Err("Team not configured yet. Set spawn first.".to_string())
}
}
Self::SetEnderchest { team } => {
let team_name = team.get_name().to_lowercase();
if let Some(t_conf) = config.locations.teams.get_mut(&team_name) {
t_conf.enderchest = player_loc;
Ok(format!("Ender Chest for team {} set", team.get_name()))
} else {
Err("Team not configured yet. Set spawn first.".to_string())
}
}
Self::AddGenerator { gen_type } => {
config.locations.generators.push(GeneratorConfig {
x: player_loc.x,
y: player_loc.y,
z: player_loc.z,
generator_type: gen_type.to_uppercase(),
});
Ok(format!("Added a {} generator at your location", gen_type.to_uppercase()))
}
Self::Save => {
// Return string representation to write to file
Ok("Config ready to save".to_string())
}
Self::Start => {
game_manager.start_game()?;
Ok("Match starting now!".to_string())
}
Self::Help => {
let mut help = String::new();
help.push_str("§d§m================ §b§lBEDWARS SETUP (RUST) §d§m================\n");
help.push_str("§d/bw setlobby §7- Set waiting lobby coordinate\n");
help.push_str("§d/bw setspawn <team> §7- Set team player spawn point\n");
help.push_str("§d/bw setbed <team> §7- Register team bed (look at block)\n");
help.push_str("§d/bw setshop <team> §7- Set shop NPC position\n");
help.push_str("§d/bw setupgrades <team> §7- Set upgrades NPC position\n");
help.push_str("§d/bw setgenerator <team> §7- Set base spawner location\n");
help.push_str("§d/bw setenderchest <team> §7- Set team Ender Chest\n");
help.push_str("§d/bw addgenerator <diamond|emerald> §7- Add generator\n");
help.push_str("§d/bw save §7- Save config file\n");
help.push_str("§d/bw start §7- Force start game immediately\n");
help.push_str("§d§m================================================\n");
Ok(help)
}
}
}
}
+70
View File
@@ -0,0 +1,70 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Loc {
pub x: f64,
pub y: f64,
pub z: f64,
pub yaw: Option<f32>,
pub pitch: Option<f32>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TeamConfig {
pub enabled: bool,
pub spawn: Loc,
pub bed: Loc,
pub generator: Loc,
pub shop: Loc,
pub upgrades: Loc,
pub enderchest: Loc,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GeneratorConfig {
pub x: f64,
pub y: f64,
pub z: f64,
#[serde(rename = "type")]
pub generator_type: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RatesConfig {
pub iron: u32,
pub gold: u32,
pub diamond: u32,
pub emerald: u32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LocationsConfig {
pub lobby: Loc,
pub teams: HashMap<String, TeamConfig>,
pub generators: Vec<GeneratorConfig>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BedwarsConfig {
#[serde(rename = "world-name")]
pub world_name: String,
#[serde(rename = "lobby-server")]
pub lobby_server: String,
#[serde(rename = "min-players")]
pub min_players: u32,
#[serde(rename = "countdown-seconds")]
pub countdown_seconds: u32,
pub rates: RatesConfig,
pub locations: LocationsConfig,
}
impl BedwarsConfig {
pub fn load_from_str(content: &str) -> Result<Self, serde_yaml::Error> {
serde_yaml::from_str(content)
}
pub fn save_to_string(&self) -> Result<String, serde_yaml::Error> {
serde_yaml::to_string(self)
}
}
+155
View File
@@ -0,0 +1,155 @@
use crate::config::Loc;
use crate::team::BedwarsTeam;
use crate::game::GameManager;
use crate::arena::{ArenaManager, BlockPos};
use crate::state::GameState;
pub struct BedwarsEvents;
impl BedwarsEvents {
pub fn handle_block_place(
arena: &mut ArenaManager,
pos: BlockPos,
block_type: &str,
is_spectator: bool,
) -> Result<Option<String>, &'static str> {
if is_spectator {
return Err("Spectators cannot place blocks");
}
if block_type == "minecraft:tnt" {
// Return command to spawn primed TNT and bypass block placement
Ok(Some("spawn_primed_tnt".to_string()))
} else {
arena.track_block(pos);
Ok(None)
}
}
pub fn handle_block_break(
arena: &mut ArenaManager,
game_manager: &mut GameManager,
pos: BlockPos,
block_type: &str,
breaker_uuid: &str,
) -> Result<Option<BedwarsTeam>, &'static str> {
let breaker = game_manager.players.get(breaker_uuid).ok_or("Player not found")?;
if breaker.is_spectator {
return Err("Spectators cannot break blocks");
}
// 1. Bed Break logic
if block_type.ends_with("_bed") {
let breaker_team = breaker.team.ok_or("You are not on a team")?;
// Search config locations for which team's bed is broken
let mut target_team = None;
for team_state in game_manager.teams.values() {
if team_state.enabled && team_state.has_bed {
// In host adapter, we would check block coordinate equality.
// For logic module, we match block team color to block type.
let bed_mat = team_state.team.get_bed_material();
if block_type == format!("minecraft:{}", bed_mat) {
target_team = Some(team_state.team);
break;
}
}
}
if let Some(broken_team) = target_team {
if breaker_team == broken_team {
return Err("You cannot break your own bed!");
}
game_manager.set_bed_broken(broken_team);
if let Some(p) = game_manager.players.get_mut(breaker_uuid) {
p.beds_broken += 1;
}
return Ok(Some(broken_team));
} else {
return Err("This bed block is not registered to a team");
}
}
// 2. Standard Block Break logic
if arena.is_player_placed(&pos) {
arena.untrack_block(&pos);
Ok(None)
} else {
Err("You can only break blocks placed by players!")
}
}
pub fn handle_void_check(
game_manager: &mut GameManager,
uuid: &str,
y_coord: f64,
) -> bool {
if game_manager.state != GameState::Playing {
return false;
}
if let Some(player) = game_manager.players.get(uuid) {
if player.is_spectator {
return false;
}
if y_coord <= -20.0 {
game_manager.handle_death(uuid);
return true;
}
}
false
}
pub fn handle_pickup_resource(
item_type: &str,
is_generator_spawned: bool,
picker_uuid: &str,
game_manager: &GameManager,
teammate_distances: Vec<(String, f64)>, // uuid -> distance squared
) -> Vec<(String, String, u32)> {
// Vec of (teammate_uuid, item_type, amount) to replicate
// Critical dupe check - only replicate resource drops spawned by a generator
if !is_generator_spawned {
return Vec::new();
}
if item_type != "minecraft:iron_ingot" &&
item_type != "minecraft:gold_ingot" &&
item_type != "minecraft:diamond" &&
item_type != "minecraft:emerald" {
return Vec::new();
}
let picker = match game_manager.players.get(picker_uuid) {
Some(p) => p,
None => return Vec::new(),
};
let picker_team = match picker.team {
Some(t) => t,
None => return Vec::new(),
};
let mut replicate_spawns = Vec::new();
for (other_uuid, distance_sq) in teammate_distances {
if other_uuid == picker_uuid {
continue;
}
if let Some(other) = game_manager.players.get(&other_uuid) {
if !other.is_spectator && other.team == Some(picker_team) {
// Check if teammate is within 3.5 blocks (12.25 blocks squared)
if distance_sq <= 12.25 {
replicate_spawns.push((other_uuid, item_type.to_string(), 1));
}
}
}
}
replicate_spawns
}
}
+201
View File
@@ -0,0 +1,201 @@
use std::collections::HashMap;
use crate::state::GameState;
use crate::team::BedwarsTeam;
use crate::generator::Generator;
use crate::arena::ArenaManager;
#[derive(Debug, Clone)]
pub struct PlayerState {
pub uuid: String,
pub name: String,
pub team: Option<BedwarsTeam>,
pub is_spectator: bool,
pub kills: u32,
pub deaths: u32,
pub beds_broken: u32,
pub respawn_timer: Option<u32>, // seconds remaining
}
#[derive(Debug, Clone)]
pub struct TeamState {
pub team: BedwarsTeam,
pub has_bed: bool,
pub enabled: bool,
}
pub struct GameManager {
pub state: GameState,
pub players: HashMap<String, PlayerState>,
pub teams: HashMap<BedwarsTeam, TeamState>,
pub countdown: Option<u32>,
pub generators: Vec<Generator>,
pub arena: ArenaManager,
pub min_players: u32,
}
impl GameManager {
pub fn new(min_players: u32, countdown_seconds: u32) -> Self {
let mut teams = HashMap::new();
for team in &[BedwarsTeam::Red, BedwarsTeam::Blue, BedwarsTeam::Purple, BedwarsTeam::Yellow] {
teams.insert(*team, TeamState {
team: *team,
has_bed: true,
enabled: false,
});
}
Self {
state: GameState::Lobby,
players: HashMap::new(),
teams,
countdown: Some(countdown_seconds),
generators: Vec::new(),
arena: ArenaManager::new(),
min_players,
}
}
pub fn join_player(&mut self, uuid: &str, name: &str) {
let state = PlayerState {
uuid: uuid.to_string(),
name: name.to_string(),
team: None,
is_spectator: false,
kills: 0,
deaths: 0,
beds_broken: 0,
respawn_timer: None,
};
self.players.insert(uuid.to_string(), state);
}
pub fn quit_player(&mut self, uuid: &str) {
self.players.remove(uuid);
if self.state == GameState::Playing {
self.check_winner();
}
}
pub fn enable_team(&mut self, team: BedwarsTeam) {
if let Some(state) = self.teams.get_mut(&team) {
state.enabled = true;
}
}
pub fn start_game(&mut self) -> Result<(), &'static str> {
let enabled_teams: Vec<BedwarsTeam> = self.teams.values()
.filter(|t| t.enabled)
.map(|t| t.team)
.collect();
if enabled_teams.len() < 2 {
return Err("At least two teams must be enabled to start the game");
}
// Assign players to enabled teams evenly
let mut team_idx = 0;
for player in self.players.values_mut() {
if player.is_spectator {
continue;
}
let team = enabled_teams[team_idx];
player.team = Some(team);
team_idx = (team_idx + 1) % enabled_teams.len();
}
self.state = GameState::Playing;
self.countdown = None;
Ok(())
}
pub fn handle_death(&mut self, uuid: &str) {
if let Some(player) = self.players.get_mut(uuid) {
player.deaths += 1;
if let Some(team) = player.team {
let has_bed = self.teams.get(&team).map(|t| t.has_bed).unwrap_or(false);
if has_bed {
player.respawn_timer = Some(5); // 5 seconds respawn queue
} else {
player.is_spectator = true;
player.respawn_timer = None;
}
}
}
self.check_winner();
}
pub fn tick_game(&mut self) -> Vec<(String, String)> {
let mut events = Vec::new();
// Handle Lobby countdown
if self.state == GameState::Lobby {
if let Some(ref mut time) = self.countdown {
if self.players.len() as u32 >= self.min_players {
if *time > 0 {
*time -= 1;
if *time == 0 {
if let Err(e) = self.start_game() {
tracing::error!("Failed to start game: {}", e);
self.countdown = Some(10); // retry in 10s
}
}
}
} else {
// Reset countdown if player count drops below min_players
*time = 30;
}
}
}
// Handle Playing respawn timers
if self.state == GameState::Playing {
for player in self.players.values_mut() {
if let Some(ref mut time) = player.respawn_timer {
if *time > 0 {
*time -= 1;
if *time == 0 {
player.respawn_timer = None;
events.push((player.uuid.clone(), "respawn".to_string()));
}
}
}
}
}
events
}
pub fn set_bed_broken(&mut self, team: BedwarsTeam) {
if let Some(state) = self.teams.get_mut(&team) {
state.has_bed = false;
}
}
pub fn check_winner(&mut self) -> Option<BedwarsTeam> {
if self.state != GameState::Playing {
return None;
}
// Find teams that still have active, non-spectator players
let mut active_teams = HashMap::new();
for player in self.players.values() {
if !player.is_spectator {
if let Some(team) = player.team {
*active_teams.entry(team).or_insert(0) += 1;
}
}
}
if active_teams.len() == 1 {
let winning_team = *active_teams.keys().next().unwrap();
self.state = GameState::Ending;
Some(winning_team)
} else if active_teams.is_empty() {
// Tie or no players left
self.state = GameState::Ending;
None
} else {
None
}
}
}
+79
View File
@@ -0,0 +1,79 @@
use crate::config::Loc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeneratorType {
IronGold,
Diamond,
Emerald,
}
pub struct Generator {
pub location: Loc,
pub gen_type: GeneratorType,
pub ticks_until_spawn: i32,
pub max_spawn_ticks: i32,
pub upgrade_level: u32,
}
impl Generator {
pub fn new(location: Loc, gen_type: GeneratorType, max_spawn_ticks: i32) -> Self {
Self {
location,
gen_type,
ticks_until_spawn: max_spawn_ticks,
max_spawn_ticks,
upgrade_level: 1,
}
}
pub fn tick(&mut self, random_roll: f64) -> Option<Vec<&'static str>> {
self.ticks_until_spawn -= 1;
if self.ticks_until_spawn <= 0 {
let mut spawned = Vec::new();
match self.gen_type {
GeneratorType::IronGold => {
spawned.push("minecraft:iron_ingot");
// Gold at 1/4 rate, modified by upgrade
if random_roll < (0.25 * self.upgrade_level as f64) {
spawned.push("minecraft:gold_ingot");
}
self.ticks_until_spawn = std::cmp::max(5, self.max_spawn_ticks - (self.upgrade_level as i32 - 1) * 3);
}
GeneratorType::Diamond => {
spawned.push("minecraft:diamond");
self.ticks_until_spawn = self.max_spawn_ticks;
}
GeneratorType::Emerald => {
spawned.push("minecraft:emerald");
self.ticks_until_spawn = self.max_spawn_ticks;
}
}
Some(spawned)
} else {
None
}
}
}
pub struct GeneratorManager {
pub generators: Vec<Generator>,
}
impl GeneratorManager {
pub fn new() -> Self {
Self {
generators: Vec::new(),
}
}
pub fn tick_all(&mut self) -> Vec<(Loc, Vec<&'static str>)> {
let mut spawns = Vec::new();
for gen in &mut self.generators {
let roll = rand::random::<f64>(); // Bypassed in tests or run directly
if let Some(items) = gen.tick(roll) {
spawns.push((gen.location.clone(), items));
}
}
spawns
}
}
+54
View File
@@ -0,0 +1,54 @@
pub mod config;
pub mod team;
pub mod state;
pub mod generator;
pub mod arena;
pub mod shop;
pub mod game;
pub mod commands;
pub mod events;
use pumpkin_plugin_api::{Context, Plugin, PluginMetadata};
use tracing::info;
pub struct BedwarsPlugin {
pub game_manager: Option<game::GameManager>,
pub config: Option<config::BedwarsConfig>,
}
impl Plugin for BedwarsPlugin {
fn new() -> Self {
Self {
game_manager: None,
config: None,
}
}
fn metadata(&self) -> PluginMetadata {
PluginMetadata {
name: "Bedwars Rust".into(),
version: env!("CARGO_PKG_VERSION").into(),
authors: vec!["Antigravity".into()],
description: "Rewrite of Bedwars plugin in Rust for Pumpkin MC".into(),
dependencies: vec![],
permissions: vec![],
}
}
fn on_load(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> {
info!("Bedwars Rust Plugin has been loaded successfully!");
// Initialize default GameManager (requires min 2 players, 30 seconds lobby time)
let game_manager = game::GameManager::new(2, 30);
self.game_manager = Some(game_manager);
Ok(())
}
fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> {
info!("Bedwars Rust Plugin has been unloaded. Goodbye!");
Ok(())
}
}
pumpkin_plugin_api::register_plugin!(BedwarsPlugin);
+118
View File
@@ -0,0 +1,118 @@
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ShopItem {
pub material: String,
pub cost_material: String,
pub cost_amount: u32,
pub display_name: String,
pub give_amount: u32,
}
impl ShopItem {
pub fn parse(config_str: &str) -> Option<Self> {
// Example format: WOOL:IRON:4:&fWhite Wool:16
let parts: Vec<&str> = config_str.split(':').collect();
if parts.len() < 4 {
return None;
}
let raw_mat = parts[0].to_uppercase();
let material = format!("minecraft:{}", raw_mat.to_lowercase());
let cost_type = parts[1].to_uppercase();
let cost_material = match cost_type.as_str() {
"IRON" => "minecraft:iron_ingot",
"GOLD" => "minecraft:gold_ingot",
"DIAMOND" => "minecraft:diamond",
"EMERALD" => "minecraft:emerald",
_ => "minecraft:iron_ingot",
}.to_string();
let cost_amount = parts[2].parse::<u32>().ok()?;
let display_name = parts[3].to_string();
let give_amount = if parts.len() >= 5 {
parts[4].parse::<u32>().unwrap_or(1)
} else {
1
};
Some(Self {
material,
cost_material,
cost_amount,
display_name,
give_amount,
})
}
}
pub struct PlayerInventory {
pub items: HashMap<String, u32>,
}
impl PlayerInventory {
pub fn new() -> Self {
Self {
items: HashMap::new(),
}
}
pub fn get_item_count(&self, item_id: &str) -> u32 {
*self.items.get(item_id).unwrap_or(&0)
}
pub fn take_items(&mut self, item_id: &str, amount: u32) -> bool {
let current = self.get_item_count(item_id);
if current >= amount {
if current == amount {
self.items.remove(item_id);
} else {
self.items.insert(item_id.to_string(), current - amount);
}
true
} else {
false
}
}
pub fn give_items(&mut self, item_id: &str, amount: u32) {
let current = self.get_item_count(item_id);
self.items.insert(item_id.to_string(), current + amount);
}
}
pub struct ShopManager {
pub categories: HashMap<String, Vec<ShopItem>>,
}
impl ShopManager {
pub fn new() -> Self {
Self {
categories: HashMap::new(),
}
}
pub fn add_item(&mut self, category: &str, item_str: &str) {
if let Some(item) = ShopItem::parse(item_str) {
self.categories.entry(category.to_string()).or_default().push(item);
}
}
pub fn attempt_purchase(
&self,
inventory: &mut PlayerInventory,
category: &str,
item_index: usize,
) -> Result<ShopItem, &'static str> {
let items = self.categories.get(category).ok_or("Category not found")?;
let item = items.get(item_index).ok_or("Item not found")?;
if inventory.take_items(&item.cost_material, item.cost_amount) {
inventory.give_items(&item.material, item.give_amount);
Ok(item.clone())
} else {
Err("Insufficient funds")
}
}
}
+6
View File
@@ -0,0 +1,6 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GameState {
Lobby,
Playing,
Ending,
}
+70
View File
@@ -0,0 +1,70 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BedwarsTeam {
Red,
Blue,
Purple,
Yellow,
}
impl BedwarsTeam {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"red" => Some(Self::Red),
"blue" => Some(Self::Blue),
"purple" => Some(Self::Purple),
"yellow" => Some(Self::Yellow),
_ => None,
}
}
pub fn get_name(&self) -> &'static str {
match self {
Self::Red => "Red",
Self::Blue => "Blue",
Self::Purple => "Purple",
Self::Yellow => "Yellow",
}
}
pub fn get_color_code(&self) -> &'static str {
match self {
Self::Red => "§c",
Self::Blue => "§9",
Self::Purple => "§5",
Self::Yellow => "§e",
}
}
pub fn get_prefix(&self) -> &'static str {
match self {
Self::Red => "§c[RED] ",
Self::Blue => "§9[BLUE] ",
Self::Purple => "§5[PURPLE] ",
Self::Yellow => "§e[YELLOW] ",
}
}
pub fn get_wool_material(&self) -> &'static str {
match self {
Self::Red => "red_wool",
Self::Blue => "blue_wool",
Self::Purple => "purple_wool",
Self::Yellow => "yellow_wool",
}
}
pub fn get_bed_material(&self) -> &'static str {
match self {
Self::Red => "red_bed",
Self::Blue => "blue_bed",
Self::Purple => "purple_bed",
Self::Yellow => "yellow_bed",
}
}
pub fn get_colorized_name(&self) -> String {
format!("{}{}", self.get_color_code(), self.get_name())
}
}