├── .gitignore ├── README.md ├── images └── logo.png ├── main.ts ├── package.json └── src ├── effect.ts ├── sandbox.ts ├── session.ts └── utils.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | pnpm-lock.yaml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastBuilder 2 | 3 | ![fb](images/logo.png) 4 | 5 | Websocket based Minecraft Bedrock Structure Generator. 6 | Currently a port of [Voxel Geometry](https://github.com/CAIMEOX/VoxelGeometry.git) in WebSocket. 7 | There is also a version using gametest framework (ScriptAPI) in [here](https://github.com/LoveCouple/VoxelGeometryMC). 8 | 9 | ## Run the server 10 | 11 | Install the dependencies by `pnpm i` or `npm i` and then execute the server by: 12 | 13 | ```bash 14 | npm run start 15 | ``` 16 | 17 | ## Basic Concepts 18 | 19 | ### Command 20 | Note that all the commands can be sent to the server by using the in-game chat. The command should start with the prefix `-`. A command is also a javascript code snippet, and **FastBuilder** behaves as a javascript runtime where you can invoke the geometry API provided by the server. 21 | 22 | ### Vector 23 | 24 | A vector represent a voxel in the Space, which has 3 components. 25 | 26 | ```js 27 | // Create a unit vector 28 | const vec: Vec3 = vec3(1, 1, 1); 29 | ``` 30 | 31 | ### Space 32 | 33 | Many Voxel Geometry functions will return a **Space** (A array of 3D vectors). 34 | 35 | ```ts 36 | const ball: Space = sphere(5, 4); 37 | ``` 38 | 39 | ## Basic Geometry 40 | 41 | ### Sphere 42 | 43 | Create a sphere with radius. 44 | 45 | ```haskell 46 | sphere :: (radius, inner_radius) -> Space 47 | sphere(5, 4) 48 | ``` 49 | 50 | ### Circle 51 | 52 | Create a circle with radius. 53 | 54 | ```haskell 55 | circle :: (radius, inner_radius) -> Space 56 | circle(5, 4) 57 | ``` 58 | 59 | ## Transformer 60 | 61 | ### scale 62 | 63 | Scale up a Space 64 | 65 | ```haskell 66 | scale :: (Space, size) -> Space 67 | ``` 68 | 69 | ### swap 70 | 71 | Change the direction of a space. 72 | 73 | ```haskell 74 | swap :: (Space, number, number) -> Space 75 | ``` 76 | 77 | ### pipe 78 | 79 | Take the point of the previous space as the origin of the next space. 80 | 81 | ```haskell 82 | pipe :: (Space_1, Space_2, ...) -> Space 83 | ``` 84 | 85 | ### diffusion 86 | 87 | Spread out points of a space by a factor. 88 | 89 | ```haskell 90 | diffusion :: (Space, factor) -> Space 91 | ``` 92 | 93 | ### move 94 | 95 | Move a space into a specific point. 96 | 97 | ```haskell 98 | move :: (Space, x, y, z) -> Space 99 | ``` 100 | 101 | ### embed 102 | 103 | Embed a space into another space 104 | 105 | ```haskell 106 | embed :: (Space, Space) -> Space 107 | ``` 108 | 109 | ### Array Generator 110 | 111 | Construct a discrete set of points. 112 | 113 | ```haskell 114 | array_gen :: (xn, yn, zn, dx, dy, dz) -> Space 115 | ``` 116 | 117 | - \_n : Count 118 | - d\_ : Interval 119 | 120 | With step function: 121 | 122 | ```haskell 123 | array_gen_fn :: (xn, yn, zn, num -> num, num -> num, num -> num) -> Space 124 | ``` 125 | 126 | ## Turtle 127 | 128 | ### Turtle2D 129 | 130 | Turtle graphics are vector graphics using a relative cursor (the "turtle") upon a Cartesian plane (x and y axis). 131 | 132 | Voxel Geometry supports basic functions of turtle graphics: 133 | 134 | ```javascript 135 | // Draw a straight with length 10 136 | const t = new Turtle2D(); 137 | t.forward(10); 138 | plot(t.getTrack()); 139 | ``` 140 | 141 | ### Turtle3D 142 | 143 | Same as Turtle2D but lives in 3D space. 144 | 145 | ## L-System 146 | 147 | An L-system or Lindenmayer system is a parallel rewriting system and a type of formal grammar.It consists of an **alphabet**, a collection of **production rules** that expand each symbol into some larger string of symbols, an initial "**axiom**" string from which to begin construction, and a **mechanism** (Such as Turtle Graphics) for translating the generated strings into geometric structures. 148 | 149 | In Voxel Geometry, you can use this function to create a Bracketed L-system: 150 | 151 | ```haskell 152 | lsystem :: (axiom, Rules, generation) -> Space 153 | ``` 154 | 155 | For instance, we can create Peano curve by using l-system. 156 | 157 | ```javascript 158 | lsystem( 159 | "X", 160 | { 161 | X: "XFYFX+F+YFXFY-F-XFYFX", 162 | Y: "YFXFY-F-XFYFX+F+YFXFY", 163 | }, 164 | 5 165 | ); 166 | ``` 167 | 168 | Voxel Geometry uses Turtle Graphics as default mechanism. 169 | 170 | ## Canvas 171 | 172 | Voxel geometry supports a part of Canvas API in browser. 173 | 174 | ## Math Interpreter 175 | 176 | ### Parametric Equation 177 | 178 | Parametric equations are commonly used to express the coordinates of the points that make up a geometric object such as a curve or surface. It includes group of quantities as functions of one or more independent variables called **parameters**. 179 | 180 | For instance, we could write down the Parametric equations of ellipse. (t is the parameter, which varies from 0 to 2\*Pi) 181 | 182 | ```javascript 183 | // a and b are constants 184 | x = a * cos(t); 185 | y = b * sin(t); 186 | ``` 187 | 188 | Express this in Voxel Geometry (step represent the changing value of the parameter): 189 | 190 | ```javascript 191 | let step = 0.1; 192 | plot( 193 | simple_parametric("5*Math.cos(t)", "0", "10*Math.sin(t)", [ 194 | "t", 195 | 0, 196 | Math.PI * 2, 197 | step, 198 | ]) 199 | ); 200 | ``` 201 | 202 | ### Expression 203 | 204 | Takes a math expression (Such as inequality) as a condition and intervals, construct a space satisfies this: 205 | 206 | ```haskell 207 | simple_equation :: (Expr, start, end, step) -> Space 208 | ``` 209 | 210 | For instance we can construct a sphere: 211 | 212 | ```javascript 213 | plot(simple_equation("x*x+y*y+z*z<=5", -5, 5, 1)); 214 | ``` 215 | 216 | ## Diffusion Limited Aggression 217 | 218 | Simulating particles undergoing a random walk due to Brownian motion cluster together to form aggregates of such particles. 219 | 220 | ### DLA2D 221 | 222 | ```haskell 223 | DLA2D :: (width, maxWalk, iterations, stepLength, temperature, stuckSpace = centerPoint) -> Space 224 | ``` 225 | 226 | - width : Width of operation space. 227 | - maxWalk : Maximum number of particles that can exist simultaneously. 228 | - iterations : Determine how many times before each particle supplement. 229 | - stepLength : Step size of particles. 230 | - temperature : The temperature of the iterative system will gradually decrease, which is related to the number of subsequent replenishment points. 231 | - stuckSpace : A collection of particles that have been fixed at the beginning. 232 | 233 | ### DLA3D 234 | 235 | Same as DLA2D but lives in 3D space. 236 | 237 | ```haskell 238 | DLA3D :: (width, maxWalk, iterations, stepLength, temperature, stuckSpace = centerPoint) -> Space 239 | ``` 240 | 241 | ## Iterated Function System 242 | 243 | An iterated function system is a finite set of mappings on a complete metric space. Iterated function systems (IFSs) are a method of constructing **fractals**. 244 | 245 | Voxel Geometry uses the classic algorithm named **Chaos Game** to compute IFS fractals. 246 | 247 | Voxel Geometry uses the representation introduced in [this website](https://cs.lmu.edu/~ray/notes/ifs/) 248 | 249 | By convention an IFS is written in rows of numbers in the form : 250 | 251 | ``` 252 | a b c d e f p 253 | ``` 254 | 255 | which describes the transform `λ(x,y).(ax+by+e,cx+dy+f)`. The value p represents the percentage of the fractal's area generated by the transform. Theoretically it is not required but if you select it well, the fractal is drawn much more efficiently. 256 | 257 | ```haskell 258 | create_IFS :: (form, width, height) -> IFS 259 | ``` 260 | 261 | Here is a classic to try: 262 | 263 | ```javascript 264 | // Create an IFS with Fractals.angle, 100000 iteration 265 | plot(create_IFS(Fractals.angle, 100, 100).run(100000)); 266 | ``` 267 | 268 | ## TODO 269 | - [ ] The notation in the document should be updated. 270 | - [ ] Load structure from NBT / MCStructure and more file formats. 271 | 272 | ## Maintainer 273 | 274 | - [**CAIMEO**](https://github.com/CAIMEOX) 275 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAIMEOX/FastBuilder/127f72a9ba3228b45c772a63c1470a4bf1fa8653/images/logo.png -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { WSServer } from "mcpews"; 2 | import Session from "./src/session"; 3 | 4 | const server = new WSServer(11451); 5 | console.log("Server started on port 11451"); 6 | server.on("client", ({ session }) => { 7 | Session.create(session); 8 | }); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastbuilder", 3 | "version": "1.0.0", 4 | "description": "Minecraft Bedrock Structure Generator based on WebSocket", 5 | "main": "Main.js", 6 | "scripts": { 7 | "start": "bun run main.ts" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/CAIMEOX/FastBuilder.git" 12 | }, 13 | "author": "CAIMEO", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/CAIMEOX/FastBuilder/issues" 17 | }, 18 | "homepage": "https://github.com/CAIMEOX/FastBuilder#readme", 19 | "dependencies": { 20 | "@pureeval/voxel-geometry": "^1.2.20", 21 | "mcpews": "^4.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/effect.ts: -------------------------------------------------------------------------------- 1 | import * as VG from "@pureeval/voxel-geometry"; 2 | import { ServerSession } from "mcpews"; 3 | type Space = VG.Vec3[]; 4 | 5 | const Vec3 = (x: number, y: number, z: number) => new VG.Vec3(x, y, z); 6 | 7 | const setBlock = 8 | (session: ServerSession) => (block: string) => (pos: VG.Vec3) => { 9 | session.sendCommand(`setblock ${pos.x} ${pos.y} ${pos.z} ${block}`); 10 | }; 11 | 12 | const overSpace = (space: Space) => (fn: (pos: VG.Vec3) => void) => { 13 | space.forEach(fn); 14 | }; 15 | 16 | const setBlocks = 17 | (session: ServerSession) => (block: string) => (space: Space) => { 18 | overSpace(space)(setBlock(session)(block)); 19 | }; 20 | 21 | function lift({ x, y, z }: { x: number; y: number; z: number }): VG.Vec3 { 22 | return Vec3(x, y, z); 23 | } 24 | 25 | export { Vec3, setBlock, overSpace, setBlocks, Space, lift }; 26 | -------------------------------------------------------------------------------- /src/sandbox.ts: -------------------------------------------------------------------------------- 1 | class Sandbox { 2 | sandbox: object; 3 | constructor(sandbox: object) { 4 | this.sandbox = sandbox; 5 | } 6 | 7 | eval(code: string): unknown { 8 | const body = `with(inside) { ${code} }`; 9 | const fn = new Function("inside", body); 10 | return fn(this.sandbox); 11 | } 12 | 13 | updateEnv(...env: object[]) { 14 | Object.assign(this.sandbox, env[0]); 15 | } 16 | } 17 | 18 | export { Sandbox }; 19 | -------------------------------------------------------------------------------- /src/session.ts: -------------------------------------------------------------------------------- 1 | import * as V from "@pureeval/voxel-geometry"; 2 | import { Space, setBlocks, Vec3, lift } from "./effect"; 3 | import { Sandbox } from "./sandbox"; 4 | import { tell_raw } from "./utils"; 5 | import { ServerSession } from "mcpews"; 6 | 7 | type Setting = { 8 | block: string; 9 | origin: V.Vec3; 10 | particle: string; 11 | }; 12 | 13 | export default class Session { 14 | session: ServerSession; 15 | history: Array = []; 16 | sandbox: Sandbox = new Sandbox({}); 17 | setting: Setting; 18 | callbacks: { [key: string]: (arg: any) => void } = {}; 19 | constructor(session: ServerSession) { 20 | session.enableEncryption(); 21 | this.session = session; 22 | this.setting = { 23 | block: "minecraft:iron_block", 24 | origin: Vec3(0, 0, 0), 25 | particle: "minecraft:explosion", 26 | }; 27 | this.welcome(); 28 | this.bind_user(); 29 | this.load_pure_functions(); 30 | this.load_effect_functions(); 31 | } 32 | 33 | static create(session: ServerSession) { 34 | return new Session(session); 35 | } 36 | 37 | welcome() { 38 | this.broadcast("Welcome to the FastBuilder!"); 39 | } 40 | 41 | load_pure_functions() { 42 | this.sandbox.updateEnv({ 43 | ...V.Generator, 44 | ...V.Exp, 45 | ...V.Transform, 46 | ...V.LSystem, 47 | ...V.IFS, 48 | ...V.DLA, 49 | vec3: Vec3, 50 | }); 51 | } 52 | 53 | load_effect_functions() { 54 | this.sandbox.updateEnv({ 55 | session: this.session, 56 | plot: this.plot, 57 | pos: () => { 58 | this.session.sendCommand("testforblock ~ ~ ~ air", ({ body }) => { 59 | this.setting.origin = body.position as V.Vec3; 60 | this.tell_raw( 61 | `Position Got: ${this.setting.origin.x}, ${this.setting.origin.y}, ${this.setting.origin.z}` 62 | ); 63 | }); 64 | }, 65 | setBlocks, 66 | tell_raw: this.tell_raw, 67 | }); 68 | } 69 | 70 | plot(space: Space, o = this.setting.origin, block = this.setting.block) { 71 | return setBlocks(this.session)(block)(V.Transform.move(space, lift(o))); 72 | } 73 | 74 | exports() { 75 | this.sandbox.updateEnv({ 76 | session: this.session, 77 | setting: this.setting, 78 | }); 79 | } 80 | 81 | bind_user() { 82 | let t = this; 83 | this.session.subscribe("PlayerMessage", ({ body }) => { 84 | const message: string = body.message as string; 85 | if (message.startsWith("-")) { 86 | const script = message.substring(1).trim(); 87 | t.exports(); 88 | try { 89 | const result = t.sandbox.eval(script); 90 | if (result) { 91 | t.tell_raw(`>> §e${result}`); 92 | } else { 93 | t.tell_raw(`>> §eSuccess`); 94 | } 95 | } catch (e) { 96 | t.tell_raw(`>> §4${e}`); 97 | } 98 | } 99 | }); 100 | } 101 | 102 | tell_raw(...message: string[]) { 103 | this.session.sendCommand(tell_raw("@s", ...message.map((m) => `§e${m}`))); 104 | } 105 | 106 | broadcast(...message: string[]) { 107 | this.session.sendCommand(tell_raw("@a", ...message.map((m) => `§e${m}`))); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | function tell_raw(Player: string, ...Message: string[]) { 2 | return `tellraw ${Player} {"rawtext":[{"text":"${now()} ${Message.join( 3 | "\n" 4 | )}"}]}`; 5 | } 6 | 7 | function now(): string { 8 | const date = new Date(); 9 | return ["[", date.toTimeString().slice(0, 8), "]"].join(""); 10 | } 11 | 12 | 13 | export { tell_raw }; 14 | --------------------------------------------------------------------------------