├── assets ├── diagram.png ├── siemcraft_logo.png ├── siemcraft_pig.png ├── siemcraft_command.png ├── siemcraft_settings.png ├── siemcraft_settings2.png ├── siemcraft_settings3.png └── diamond_talk_sharable.pdf ├── .gitignore ├── siemcraft_addon_behavior ├── pack_icon.png ├── package.json ├── .vscode │ └── launch.json ├── manifest.json ├── package-lock.json ├── spawn_rules │ └── bear.json ├── scripts │ └── server │ │ └── server_scriptapi.js └── entities │ ├── chicken.json │ ├── cow.json │ ├── bear.json │ ├── pig.json │ ├── spider.json │ └── panda.json ├── siemcraft_addon_gametest ├── pack_icon.png ├── manifest.json └── scripts │ └── script_gametest.js ├── siemcraft_addon_resource ├── pack_icon.png ├── textures │ └── entity │ │ └── bear.png ├── manifest.json └── entity │ └── bear.entity.json ├── run.ps1 ├── rules ├── security.yml ├── where.yml └── whoami.yml ├── example_agent_configs ├── sealighter_config.json └── sysmon_config.xml ├── src ├── stringset.go ├── main.go ├── eventlogger.go ├── rules.go └── minecraft.go ├── go.mod ├── .github └── workflows │ ├── main.yml │ └── release.yml ├── test_websocket.py ├── README.md └── go.sum /assets/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/diagram.png -------------------------------------------------------------------------------- /assets/siemcraft_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_logo.png -------------------------------------------------------------------------------- /assets/siemcraft_pig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_pig.png -------------------------------------------------------------------------------- /assets/siemcraft_command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_command.png -------------------------------------------------------------------------------- /assets/siemcraft_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_settings.png -------------------------------------------------------------------------------- /assets/siemcraft_settings2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_settings2.png -------------------------------------------------------------------------------- /assets/siemcraft_settings3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/siemcraft_settings3.png -------------------------------------------------------------------------------- /assets/diamond_talk_sharable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/assets/diamond_talk_sharable.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | *.mcpack 3 | rulesbad 4 | 5 | # Used for debugging scripts 6 | siemcraft_addon_behavior/node_modules 7 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/siemcraft_addon_behavior/pack_icon.png -------------------------------------------------------------------------------- /siemcraft_addon_gametest/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/siemcraft_addon_gametest/pack_icon.png -------------------------------------------------------------------------------- /siemcraft_addon_resource/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/siemcraft_addon_resource/pack_icon.png -------------------------------------------------------------------------------- /siemcraft_addon_resource/textures/entity/bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathtofile/siemcraft/HEAD/siemcraft_addon_resource/textures/entity/bear.png -------------------------------------------------------------------------------- /run.ps1: -------------------------------------------------------------------------------- 1 | # Build 2 | $ErrorActionPreference = "Stop" 3 | . "$PSScriptRoot\build.ps1" 4 | 5 | # Run 6 | "$PSScriptRoot\bin\siemcraft.exe -rules $PSScriptRoot\rules" 7 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@types/mojang-gametest": "^0.1.3", 4 | "@types/mojang-minecraft": "^0.1.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "minecraft-js", 6 | "request": "attach", 7 | "name": "Wait for Minecraft Debug Connections", 8 | "mode": "listen", 9 | "localRoot": "${workspaceFolder}/", 10 | "port": 19144 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /rules/security.yml: -------------------------------------------------------------------------------- 1 | title: Security Logon 2 | id: bf10b06b-7dff-44d8-abd3-e07627253797 3 | status: experimental 4 | description: A user's local group membership was enumerated. 5 | author: pathtofile 6 | date: 2022/01/15 7 | references: 8 | - https://blog.tofile.dev 9 | logsource: 10 | product: windows 11 | service: security 12 | detection: 13 | selection: 14 | EventID: 15 | - 4798 16 | condition: selection 17 | falsepositives: 18 | - unknown 19 | level: low 20 | -------------------------------------------------------------------------------- /example_agent_configs/sealighter_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_properties": { 3 | "session_name": "Sealighter-Kernel-Process-Start", 4 | "output_format": "event_log" 5 | }, 6 | "kernel_traces": [ 7 | { 8 | "trace_name": "kernel_proc_start_trace", 9 | "provider_name": "process", 10 | "filters": { 11 | "any_of": { 12 | "opcode_is": 1 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /rules/where.yml: -------------------------------------------------------------------------------- 1 | title: Where Execution 2 | id: a9b9ff07-24d0-4f21-9af6-035aa26fee98 3 | status: experimental 4 | description: Where.exe runs 5 | references: 6 | - https://blog.tofile.dev 7 | tags: 8 | - attack.execution 9 | author: pathtofile 10 | date: 2022/01/15 11 | logsource: 12 | category: process_creation 13 | product: windows 14 | detection: 15 | selection: 16 | Image|endswith: '\where.exe' 17 | condition: selection 18 | falsepositives: 19 | - unknown 20 | level: low 21 | -------------------------------------------------------------------------------- /rules/whoami.yml: -------------------------------------------------------------------------------- 1 | title: Whoami Execution 2 | id: 36de6a23-651e-485a-ba69-3966d66707af 3 | status: experimental 4 | description: Whoami.exe runs 5 | references: 6 | - https://blog.tofile.dev 7 | tags: 8 | - attack.execution 9 | author: pathtofile 10 | date: 2022/01/15 11 | logsource: 12 | category: process_creation 13 | product: windows 14 | detection: 15 | selection: 16 | Image|endswith: '\whoami.exe' 17 | condition: selection 18 | falsepositives: 19 | - unknown 20 | level: high 21 | -------------------------------------------------------------------------------- /siemcraft_addon_resource/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "description": "SIEMCraft Resource", 5 | "name": "SIEMCraft Resource", 6 | "uuid": "9b761fbc-67d0-4432-a2da-59e52dbafc2e", 7 | "version": [1, 0, 0], 8 | "min_engine_version": [ 1, 18, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "description": "SIEMCraft Resource", 13 | "type": "resources", 14 | "uuid": "964aca52-81a3-4884-8238-c0cd7b3740cd", 15 | "version": [1, 0, 0] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /example_agent_configs/sysmon_config.xml: -------------------------------------------------------------------------------- 1 | 2 | md5,sha256 3 | 4 | 5 | 6 | 7 | 8 | 9 | whoami.exe 10 | where.exe 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/stringset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Used to handle channels as an array in the commandline arguments 9 | type stringSet []string 10 | 11 | type Value interface { 12 | String() string 13 | Set(string) error 14 | } 15 | 16 | func (i *stringSet) String() string { 17 | return fmt.Sprintf("%s", *i) 18 | } 19 | 20 | func (i *stringSet) Set(values string) error { 21 | // Split by comman, but only add new values 22 | for _, value := range strings.Split(values, ",") { 23 | for _, channel := range *i { 24 | if value == channel { 25 | return nil 26 | } 27 | } 28 | *i = append(*i, []string{value}...) 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "description": "SIEMCraft Behavior", 5 | "name": "SIEMCraft Behavior", 6 | "uuid": "3325c3d1-7424-45d1-af5c-bcfefff938b3", 7 | "version": [1, 0, 0], 8 | "min_engine_version": [ 1, 18, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "description": "SIEMCraft Behavior", 13 | "type": "data", 14 | "uuid": "c2db45b6-b6cc-4809-a079-313c3e1a9471", 15 | "version": [1, 0, 0] 16 | } 17 | ], 18 | "dependencies": [ 19 | { 20 | "uuid": "9b761fbc-67d0-4432-a2da-59e52dbafc2e", 21 | "version": [1, 0, 0] 22 | }, 23 | { 24 | "uuid": "b26a4d4c-afdf-4690-88f8-931846312678", 25 | "version": [ 0, 1, 0 ] 26 | }, 27 | { 28 | "uuid": "6f4b6893-1bb6-42fd-b458-7fa3d0c89616", 29 | "version": [ 0, 1, 0 ] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /siemcraft_addon_gametest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "description": "SIEMCraft GameTest", 5 | "name": "SIEMCraft GameTest", 6 | "uuid": "408a9640-e490-4e9b-9b6e-88f63e5eb27a", 7 | "version": [1, 0, 0], 8 | "min_engine_version": [ 1, 18, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "description": "SIEMCraft GameTest Script", 13 | "type": "script", 14 | "language": "javascript", 15 | "uuid": "ead57a90-41fc-4f3b-8e1a-ccc64c99da0c", 16 | "version": [1, 0, 0], 17 | "entry": "scripts/script_gametest.js" 18 | } 19 | ], 20 | "dependencies": [ 21 | { 22 | "uuid": "9b761fbc-67d0-4432-a2da-59e52dbafc2e", 23 | "version": [1, 0, 0] 24 | }, 25 | { 26 | "uuid": "b26a4d4c-afdf-4690-88f8-931846312678", 27 | "version": [ 0, 1, 0 ] 28 | }, 29 | { 30 | "uuid": "6f4b6893-1bb6-42fd-b458-7fa3d0c89616", 31 | "version": [ 0, 1, 0 ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pathtofile/siemcraft 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/0xrawsec/golang-win32 v1.0.10 7 | github.com/bradleyjkemp/sigma-go v0.2.8 8 | github.com/pathtofile/mcwss v1.2.2 9 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 10 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 11 | ) 12 | 13 | require ( 14 | github.com/0xrawsec/golang-utils v1.1.8 // indirect 15 | github.com/PaesslerAG/gval v1.0.0 // indirect 16 | github.com/PaesslerAG/jsonpath v0.1.1 // indirect 17 | github.com/alecthomas/participle v0.7.1 // indirect 18 | github.com/google/uuid v1.1.0 // indirect 19 | github.com/gorilla/websocket v1.4.0 // indirect 20 | github.com/mattn/go-colorable v0.1.12 // indirect 21 | github.com/onsi/ginkgo v1.16.5 // indirect 22 | github.com/onsi/gomega v1.17.0 // indirect 23 | github.com/sergi/go-diff v1.0.0 // indirect 24 | github.com/stretchr/testify v1.7.0 // indirect 25 | github.com/yudai/gojsondiff v1.0.0 // indirect 26 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect 27 | github.com/yudai/pp v2.0.1+incompatible // indirect 28 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /siemcraft_addon_resource/entity/bear.entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.8.0", 3 | "minecraft:client_entity": { 4 | "description": { 5 | "identifier": "siemcraft:bear", 6 | "materials": { 7 | "default": "polar_bear" 8 | }, 9 | "textures": { 10 | "default": "textures/entity/bear" 11 | }, 12 | "geometry": { 13 | "default": "geometry.polarbear" 14 | }, 15 | "animations": { 16 | "walk": "animation.quadruped.walk", 17 | "move": "animation.polarbear.move", 18 | "look_at_target": "animation.common.look_at_target", 19 | "baby_transform": "animation.polarbear.baby_transform" 20 | }, 21 | "scripts": { 22 | "scale": "1.2" 23 | }, 24 | "animation_controllers": [ 25 | { 26 | "move": "controller.animation.polarbear.move" 27 | }, 28 | { 29 | "baby": "controller.animation.polarbear.baby" 30 | } 31 | ], 32 | "render_controllers": [ 33 | "controller.render.polarbear" 34 | ], 35 | "spawn_egg": { 36 | "texture": "spawn_egg", 37 | "texture_index": 37 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: windows-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | 19 | - name: Install Go 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: '1.17.6' 23 | 24 | - name: Build binary 25 | run: go build -o .\bin\siemcraft.exe ./src 26 | 27 | - name: Build Behavior addon 28 | run: Compress-Archive -Path .\siemcraft_addon_behavior -DestinationPath .\bin\siemcraft_addon_behavior.mcpack -Force 29 | 30 | - name: Build Resource addon 31 | run: Compress-Archive -Path .\siemcraft_addon_resource -DestinationPath .\bin\siemcraft_addon_resource.mcpack -Force 32 | 33 | - name: Build GeteTest addon 34 | run: Compress-Archive -Path .\siemcraft_addon_gametest -DestinationPath .\bin\siemcraft_addon_gametest.mcpack -Force 35 | 36 | - name: Package addon 37 | run: Compress-Archive -Path .\bin\*.mcpack -DestinationPath .\bin\siemcraft.mcaddon -Force 38 | -------------------------------------------------------------------------------- /siemcraft_addon_gametest/scripts/script_gametest.js: -------------------------------------------------------------------------------- 1 | import { world, BlockLocation, GameMode } from "mojang-minecraft"; 2 | import * as GameTest from "mojang-gametest"; 3 | 4 | const dim = world.getDimension("overworld"); 5 | 6 | function sendMessage(message) { 7 | dim.runCommand(`tell @a "${message}"`); 8 | } 9 | 10 | world.events.dataDrivenEntityTriggerEvent.subscribe((eventData) => { 11 | var event_name = eventData.id; 12 | // Entity object, see: https://docs.microsoft.com/en-us/minecraft/creator/scriptapi/mojang-minecraft/entity 13 | var entity = eventData.entity; 14 | 15 | // Ignore other events 16 | // We can also filter out things when calling .subscribe(), but for now do it this way 17 | if (event_name != "special_death_event" && event_name != "special_death_event_diamond") { 18 | return; 19 | } 20 | 21 | // Get JSON event from tag 22 | var eventb64 = entity.getTags()[0]; 23 | var prefix = "[SIEMCRAFT]"; 24 | if (!eventb64.startsWith(prefix)) { 25 | return; 26 | } 27 | eventb64 = eventb64.slice(prefix.length); 28 | 29 | // Send message as a JSON string 30 | var weapon = "other_weapon"; 31 | if (event_name == "special_death_event_diamond") { 32 | weapon = "diamond_sword"; 33 | } 34 | var message = prefix + `{"eventb64": "${eventb64}", "item": "${weapon}", "projectile": "" }`; 35 | sendMessage(message); 36 | }); 37 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "siemcraft_addon_behavior", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@types/mojang-gametest": "^0.1.3", 9 | "@types/mojang-minecraft": "^0.1.3" 10 | } 11 | }, 12 | "node_modules/@types/mojang-gametest": { 13 | "version": "0.1.3", 14 | "resolved": "https://registry.npmjs.org/@types/mojang-gametest/-/mojang-gametest-0.1.3.tgz", 15 | "integrity": "sha512-qQkdymiWX60g2Y4ObpUKqTCXMgWP3WIH8jr7knvmfgudZwGMs26lbZWQZBJzZtu09sPwcJZKGbuUx/sbeCLnpg==", 16 | "dependencies": { 17 | "@types/mojang-minecraft": "*" 18 | } 19 | }, 20 | "node_modules/@types/mojang-minecraft": { 21 | "version": "0.1.3", 22 | "resolved": "https://registry.npmjs.org/@types/mojang-minecraft/-/mojang-minecraft-0.1.3.tgz", 23 | "integrity": "sha512-i6YWCGcAVAyyRr9mVaSvJlrnMQYXa23A4LnxI3vhpN9Osr0lx3gehu64nn1FiwpYf/JocMlattF+VePk0f3YVg==" 24 | } 25 | }, 26 | "dependencies": { 27 | "@types/mojang-gametest": { 28 | "version": "0.1.3", 29 | "resolved": "https://registry.npmjs.org/@types/mojang-gametest/-/mojang-gametest-0.1.3.tgz", 30 | "integrity": "sha512-qQkdymiWX60g2Y4ObpUKqTCXMgWP3WIH8jr7knvmfgudZwGMs26lbZWQZBJzZtu09sPwcJZKGbuUx/sbeCLnpg==", 31 | "requires": { 32 | "@types/mojang-minecraft": "*" 33 | } 34 | }, 35 | "@types/mojang-minecraft": { 36 | "version": "0.1.3", 37 | "resolved": "https://registry.npmjs.org/@types/mojang-minecraft/-/mojang-minecraft-0.1.3.tgz", 38 | "integrity": "sha512-i6YWCGcAVAyyRr9mVaSvJlrnMQYXa23A4LnxI3vhpN9Osr0lx3gehu64nn1FiwpYf/JocMlattF+VePk0f3YVg==" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/spawn_rules/bear.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.8.0", 3 | "minecraft:spawn_rules": { 4 | "description": { 5 | "identifier": "siemcraft:bear", 6 | "population_control": "animal" 7 | }, 8 | "conditions": [ 9 | { 10 | "minecraft:spawns_on_surface": {}, 11 | "minecraft:spawns_on_block_filter": [ 12 | "minecraft:grass", 13 | "minecraft:podzol", 14 | "minecraft:dirt" 15 | ], 16 | "minecraft:brightness_filter": { 17 | "min": 7, 18 | "max": 15, 19 | "adjust_for_weather": false 20 | }, 21 | "minecraft:weight": { 22 | "default": 1 23 | }, 24 | "minecraft:herd": { 25 | "min_size": 1, 26 | "max_size": 2, 27 | "event":"minecraft:entity_born", 28 | "event_skip_count": 1 29 | }, 30 | 31 | "minecraft:biome_filter": [ 32 | {"test": "has_biome_tag", "operator":"==", "value": "taiga"} 33 | ] 34 | }, 35 | { 36 | "minecraft:spawns_on_surface": {}, 37 | "minecraft:spawns_on_block_filter": "minecraft:grass", 38 | "minecraft:brightness_filter": { 39 | "min": 7, 40 | "max": 15, 41 | "adjust_for_weather": false 42 | }, 43 | "minecraft:weight": { 44 | "default": 5 45 | }, 46 | "minecraft:herd": { 47 | "min_size": 1, 48 | "max_size": 2, 49 | "event":"minecraft:entity_born", 50 | "event_skip_count": 1 51 | }, 52 | 53 | "minecraft:biome_filter": [ 54 | {"test": "has_biome_tag", "operator":"==", "value": "taiga"} 55 | ] 56 | } 57 | ] 58 | } 59 | } -------------------------------------------------------------------------------- /test_websocket.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from ast import parse 3 | import asyncio 4 | import websockets 5 | import json 6 | from uuid import uuid4 7 | 8 | EVENT_TYPE = "PlayerMessage" 9 | 10 | async def mineproxy(websocket, _): 11 | print('Connected') 12 | 13 | # Tell Minecraft to send all chat messages. Required once after Minecraft starts 14 | await websocket.send( 15 | json.dumps({ 16 | "header": { 17 | "version": 1, # We're using the version 1 message protocol 18 | "requestId": str(uuid4()), # A unique ID for the request 19 | "messageType": "commandRequest", # This is a request ... 20 | "messagePurpose": "subscribe" # ... to subscribe to ... 21 | }, 22 | "body": { 23 | "eventName": EVENT_TYPE 24 | }, 25 | })) 26 | 27 | try: 28 | # When MineCraft sends a message (e.g. on player chat), print it. 29 | async for msg in websocket: 30 | msg = json.loads(msg) 31 | print(json.dumps(msg, indent=2)) 32 | except websockets.exceptions.ConnectionClosedError: 33 | print('Disconnected from MineCraft') 34 | 35 | def main(): 36 | global EVENT_TYPE 37 | parser = argparse.ArgumentParser("Minecraft websocket subscriber") 38 | parser.add_argument("--host", default="localhost", help="host to listen on, default localhost") 39 | parser.add_argument("--port", default=8000, help="port to listen on, default 8000") 40 | parser.add_argument("--event", default=EVENT_TYPE, help="event type to subscript to, defauly 'PlayerMessage'") 41 | args = parser.parse_args() 42 | EVENT_TYPE = args.event 43 | 44 | start_server = websockets.serve(mineproxy, host=args.host, port=args.port) 45 | print(f'Ready. On MineCraft, type /connect {args.host}:{args.port}') 46 | 47 | asyncio.get_event_loop().run_until_complete(start_server) 48 | asyncio.get_event_loop().run_forever() 49 | 50 | if __name__ == "__main__": 51 | main() 52 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/scripts/server/server_scriptapi.js: -------------------------------------------------------------------------------- 1 | var system = server.registerSystem(0,0); 2 | 3 | system.initialize = function() { 4 | this.listenForEvent("minecraft:entity_death", (eventData) => this.onEntityDeath(eventData)); 5 | }; 6 | 7 | // Helper function to tell as message to be picked up by 8 | // the websocket in a 'PlayerMessage' event 9 | system.sendMessage = function (message) { 10 | let ExecuteEventData = this.createEventData("minecraft:execute_command"); 11 | ExecuteEventData.data.command = "/tell @a \"" + message + "\""; 12 | this.broadcastEvent("minecraft:execute_command", ExecuteEventData); 13 | }; 14 | 15 | system.onEntityDeath = function (eventData) { 16 | // Only look for entities killed by the player 17 | if (eventData.data.killer.__identifier__ != "minecraft:player") { 18 | return; 19 | } 20 | // Only look for entities killed with a name 21 | let killed_name = this.getComponent(eventData.data.entity, "minecraft:nameable"); 22 | if (killed_name == null) { 23 | return; 24 | } 25 | 26 | // Get weapon in main hand, assume it was what was used to kill it 27 | let handContainer = system.getComponent(eventData.data.killer, "minecraft:hand_container"); 28 | let mainHandItem = handContainer.data[0]; 29 | 30 | // If there was a projectile, get that projectile_type 31 | let projectile = "" 32 | if (eventData.data.projectile_type != null && eventData.data.projectile_type !== "undefined") { 33 | projectile = eventData.data.projectile_type 34 | } 35 | 36 | // Get the raw event JSON we saved as a tag 37 | let tags = system.getComponent(eventData.data.entity, "minecraft:tag"); 38 | if (tags == null) { 39 | return; 40 | } 41 | 42 | let eventb64 = tags.data[0]; 43 | var prefix = "[SIEMCRAFT]"; 44 | if (!eventb64.startsWith(prefix)) { 45 | return; 46 | } 47 | eventb64 = eventb64.slice(prefix.length); 48 | 49 | // Send message as a JSON string 50 | let message = prefix + '{"eventb64": "' + eventb64 + '",\n"item":"' + mainHandItem.item + '",\n"projectile": "' + projectile + '"}' 51 | this.sendMessage(message); 52 | }; 53 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | ) 10 | 11 | var channels stringSet 12 | 13 | func main() { os.Exit(mainReturnWithCode()) } 14 | func mainReturnWithCode() int { 15 | rulesDir := flag.String("rules", ".\\rules", "Folder containing SIGMA rules") 16 | bindAddress := flag.String("bind", "127.0.0.1", "Address to bind websocket to") 17 | bindPort := flag.String("port", "8000", "Port to bind websocket to") 18 | fakeEvents := flag.Bool("fakeEvents", false, "Don't subscript to event logs, just fake generate them") 19 | noKill := flag.Bool("noKill", false, "Never attempt to kill a process") 20 | flag.Var(&channels, "channels", "Comma-seperated list of event logs to subscribe to\n(default [\"Microsoft-Windows-Sysmon/Operational\", \"Security\"])") 21 | flag.Parse() 22 | 23 | // Set default channels if none specified 24 | if len(channels) == 0 { 25 | channels = append(channels, []string{"Microsoft-Windows-Sysmon/Operational", "Security"}...) 26 | } 27 | 28 | // Load SIGMA rules 29 | err := ParseRules(*rulesDir) 30 | if err != nil { 31 | fmt.Printf("[*] Error Parsing Rules: %s\n", err.Error()) 32 | return 1 33 | } 34 | 35 | if *fakeEvents { 36 | // Just fake them, and don't kill 37 | *noKill = true 38 | FireFakeEvents() 39 | } else { 40 | // Start getting events from Event logs 41 | err = StartEventSubscription(channels) 42 | if err != nil { 43 | fmt.Printf("[*] Error Starting Event Subscription: %s\n", err.Error()) 44 | return 2 45 | } 46 | defer StopEventSubscription() 47 | } 48 | 49 | // Start Minecraft websocket to handle requests 50 | err = StartMinecraftWebsocket(*bindAddress, *bindPort, *noKill) 51 | if err != nil { 52 | fmt.Printf("[*] Error Starting Minecraft websocket: %s\n", err.Error()) 53 | return 1 54 | } 55 | return 0 56 | } 57 | 58 | func init() { 59 | c := make(chan os.Signal) 60 | // This works on Windows, weird... 61 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 62 | go func() { 63 | <-c 64 | // Ensure we cleanup as we quit 65 | StopEventSubscription() 66 | os.Exit(0) 67 | }() 68 | } 69 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | 18 | - name: Install Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: '1.17.6' 22 | 23 | - name: Build binary 24 | run: go build -o .\bin\siemcraft.exe ./src 25 | 26 | - name: Build Behavior addon 27 | run: Compress-Archive -Path .\siemcraft_addon_behavior -DestinationPath .\bin\siemcraft_addon_behavior.mcpack -Force 28 | 29 | - name: Build Resource addon 30 | run: Compress-Archive -Path .\siemcraft_addon_resource -DestinationPath .\bin\siemcraft_addon_resource.mcpack -Force 31 | 32 | - name: Build GeteTest addon 33 | run: Compress-Archive -Path .\siemcraft_addon_gametest -DestinationPath .\bin\siemcraft_addon_gametest.mcpack -Force 34 | 35 | - name: Package addon 36 | run: Compress-Archive -Path .\bin\*.mcpack -DestinationPath .\bin\siemcraft.mcaddon -Force 37 | 38 | - name: Create release 39 | id: create_release 40 | uses: actions/create-release@v1 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | with: 44 | tag_name: ${{ github.ref }} 45 | release_name: Release ${{ github.ref }} 46 | draft: false 47 | prerelease: false 48 | 49 | - name: Upload binary 50 | uses: actions/upload-release-asset@v1 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | with: 54 | upload_url: ${{ steps.create_release.outputs.upload_url }} 55 | asset_path: ./bin/siemcraft.exe 56 | asset_name: siemcraft.exe 57 | asset_content_type: application/octet-stream 58 | 59 | - name: Upload addon 60 | uses: actions/upload-release-asset@v1 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | upload_url: ${{ steps.create_release.outputs.upload_url }} 65 | asset_path: ./bin/siemcraft.mcaddon 66 | asset_name: siemcraft.mcaddon 67 | asset_content_type: application/octet-stream 68 | -------------------------------------------------------------------------------- /src/eventlogger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/0xrawsec/golang-win32/win32/wevtapi" 12 | "github.com/bradleyjkemp/sigma-go" 13 | "golang.org/x/sys/windows" 14 | ) 15 | 16 | var waitGroup sync.WaitGroup 17 | var eventProvider *wevtapi.PullEventProvider = nil 18 | 19 | type SealighterMessage struct { 20 | Header map[string]interface{} `json:"header"` 21 | Properties map[string]interface{} `json:"properties"` 22 | PropertyTypes map[string]interface{} `json:"property_types"` 23 | } 24 | 25 | func IsAdmin() (bool, error) { 26 | // Check if current process has privliges 27 | // to get event logs as a fail-fast 28 | var token windows.Token 29 | err := windows.OpenProcessToken( 30 | windows.CurrentProcess(), 31 | windows.TOKEN_QUERY, 32 | &token, 33 | ) 34 | if err != nil { 35 | return false, err 36 | } 37 | 38 | return token.IsElevated(), nil 39 | } 40 | 41 | func StartEventSubscription(channels []string) error { 42 | fmt.Println("[e] Starting event subscription") 43 | 44 | // Only run if user is Elevated 45 | isAdmin, err := IsAdmin() 46 | if err != nil { 47 | return err 48 | } 49 | if !isAdmin { 50 | return errors.New("getting Event logs requires elevation") 51 | } 52 | 53 | fmt.Printf("[e] Subscribing to Event Log channels:\n") 54 | for _, channel := range channels { 55 | fmt.Printf("[e] - %s\n", channel) 56 | } 57 | 58 | // Creat waitGroup to keep track of goroutine 59 | waitGroup = sync.WaitGroup{} 60 | eventProvider = wevtapi.NewPullEventProvider() 61 | waitGroup.Add(1) 62 | go func() { 63 | // Make a call to wevtapi to subscive to event log channels 64 | fetchFlags := wevtapi.EvtSubscribeToFutureEvents 65 | for e := range eventProvider.FetchEvents(channels, fetchFlags) { 66 | // Check event against SIGMA rules 67 | j := e.ToJSONEvent() 68 | event := j.Event.EventData 69 | channel := strings.ToLower(j.Event.System.Channel) 70 | var rule *sigma.Rule 71 | 72 | // Add fields that Sigma might want to check 73 | event["Computer"] = j.Event.System.Computer 74 | event["EventID"] = j.Event.System.EventID 75 | event["Provider_Name"] = j.Event.System.Provider.Name 76 | 77 | if channel == "application" || channel == "security" || channel == "system" { 78 | // Check standard Event Log Channels 79 | rule, err = CheckRules(event, channel) 80 | if err != nil { 81 | continue 82 | } 83 | } else if strings.Contains(channel, "sealighter") { 84 | // Need to convert event to be like Sysmon 85 | message := SealighterMessage{} 86 | err := json.Unmarshal([]byte(event["json"]), &message) 87 | if err != nil { 88 | continue 89 | } 90 | 91 | // For now only add the 'Properties' and not the ETW Header 92 | for k, v := range message.Properties { 93 | // Rename 'ImageFileName' to 'Image' 94 | // to match with Sysmon 95 | if k == "ImageFileName" { 96 | k = "Image" 97 | v = fmt.Sprintf("\\%s", v) 98 | } 99 | event[k] = fmt.Sprint(v) 100 | } 101 | // Delete the json string as we no longet need it 102 | delete(event, "json") 103 | 104 | // Now we can check the rule 105 | // First check if there's any sealighter specific 106 | // SIGMA rules (unlikley), then if not assume a process-creation 107 | // event 108 | rule, err = CheckRules(event, "sealighter") 109 | if err != nil { 110 | continue 111 | } 112 | if rule == nil { 113 | rule, err = CheckRules(event, "process_creation") 114 | if err != nil { 115 | continue 116 | } 117 | } 118 | } else { 119 | // Otherwise assume Sysmon 120 | channel = "sysmon" 121 | eventID, err := strconv.Atoi(j.Event.System.EventID) 122 | if err != nil { 123 | continue 124 | } 125 | switch eventID { 126 | case 1: 127 | // Process 128 | rule, err = CheckRules(event, "process_creation") 129 | if err != nil { 130 | return 131 | } 132 | default: 133 | // Unhandled event type 134 | continue 135 | } 136 | } 137 | 138 | // If a match, raise alert in Minecraft 139 | if rule != nil { 140 | // Remove fields we don't need to see in Minecraft 141 | delete(event, "EventID") 142 | delete(event, "Provider_Name") 143 | RaiseAlert(event, rule, channel) 144 | } 145 | } 146 | waitGroup.Done() 147 | }() 148 | 149 | return nil 150 | } 151 | 152 | func StopEventSubscription() { 153 | if eventProvider != nil { 154 | fmt.Println("[e] Stopping event subscription") 155 | eventProvider.Stop() 156 | waitGroup.Wait() 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/rules.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "math/rand" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "time" 13 | 14 | "github.com/bradleyjkemp/sigma-go" 15 | "github.com/bradleyjkemp/sigma-go/evaluator" 16 | ) 17 | 18 | var RuleCategories []string = []string{"application", "security", "system", "process_creation", "file_create", "image_load", "driver_load", "network_connection", "dns", "registry_event", "sealighter"} 19 | var Rules map[string][]sigma.Rule 20 | 21 | func checkRule(event map[string]string, category string, rule sigma.Rule) (evaluator.Result, error) { 22 | // Sigma-Go uses runtime panics for things like "unsupported modifier re" 23 | defer func() { recover() }() 24 | 25 | ruleEvaluator := evaluator.ForRule(rule) 26 | result, err := ruleEvaluator.Matches(context.Background(), event) 27 | if err != nil { 28 | fmt.Printf("Failed to evaluate rule: %s", err.Error()) 29 | return evaluator.Result{Match: false}, err 30 | } 31 | return result, nil 32 | } 33 | 34 | func CheckRules(event map[string]string, category string) (*sigma.Rule, error) { 35 | for _, rule := range Rules[category] { 36 | // Check rule, handle runtime panics from library 37 | result, err := checkRule(event, category, rule) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | if result.Match { 43 | return &rule, nil 44 | } 45 | } 46 | return nil, nil 47 | } 48 | 49 | func ParseRules(rulesDir string) error { 50 | fmt.Printf("[r] Parsing SIGMA rules from: %s\n", rulesDir) 51 | 52 | // Create global rules slice 53 | Rules = make(map[string][]sigma.Rule, len(RuleCategories)) 54 | for _, category := range RuleCategories { 55 | Rules[category] = make([]sigma.Rule, 0, 10) 56 | } 57 | ruleCount := 0 58 | 59 | // Get rules from directory 60 | err := filepath.Walk(rulesDir, func(path string, info os.FileInfo, err error) error { 61 | if err != nil { 62 | return err 63 | } 64 | if !info.IsDir() { 65 | contents, err := ioutil.ReadFile(path) 66 | if err != nil { 67 | fmt.Printf("Failed to open rule %s: %s\n", path, err.Error()) 68 | return nil 69 | } 70 | rule, err := sigma.ParseRule(contents) 71 | if err != nil { 72 | fmt.Printf("Failed to parse rule %s: %s\n", path, err.Error()) 73 | return nil 74 | } 75 | // Only Windows rules count 76 | if rule.Logsource.Product != "windows" { 77 | return nil 78 | } 79 | 80 | // Rules either have a Service or a Category 81 | ruleType := strings.TrimSpace(rule.Logsource.Service + rule.Logsource.Category) 82 | if ruleType == "dns_query" { 83 | ruleType = "dns" 84 | } 85 | switch ruleType { 86 | case 87 | // Services 88 | "application", 89 | "security", 90 | "system", 91 | // Categories 92 | "process_creation", 93 | "file_create", 94 | "image_load", 95 | "driver_load", 96 | "network_connection", 97 | "dns", 98 | "registry_event": 99 | // 100 | fmt.Printf("[r] Found rule: %s\n", rule.Title) 101 | Rules[ruleType] = append(Rules[ruleType], rule) 102 | ruleCount++ 103 | default: 104 | fmt.Printf("Ignoring Rule for bad service/category: %s\n", rule.Title) 105 | } 106 | } 107 | return nil 108 | }) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | // Only run if we have at least one valid rule 114 | if ruleCount == 0 { 115 | errString := fmt.Sprintf("No valid rules found in %s", rulesDir) 116 | return errors.New(errString) 117 | } 118 | fmt.Printf("[r] Number of rules found: %d\n", ruleCount) 119 | return nil 120 | } 121 | 122 | // Used for Debugging only 123 | func FireFakeEvents() { 124 | // First get a list of all the rules 125 | allRules := make([]sigma.Rule, 0) 126 | for _, category := range RuleCategories { 127 | for _, rule := range Rules[category] { 128 | // Overwrite category (which we don't use) 129 | // for our own purposes 130 | rule.Logsource.Category = category 131 | allRules = append(allRules, rule) 132 | } 133 | } 134 | 135 | // Get list of possible fake values 136 | selfImage, _ := os.Executable() 137 | imageNames := []string{ 138 | "C:\\Windows\\System32\\whoami.exe", 139 | "C:\\Windows\\System32\\lsass.exe", 140 | "C:\\Windows\\System32\\svchost.exe", 141 | "C:\\Windows\\System32\\cmd.exe", 142 | "C:\\Windows\\System32\\powershell.exe", 143 | "C:\\Windows\\System32\\inetsrv\\w3wp.exe", 144 | "C:\\Python27\\python.exe", 145 | "C:\\dodgy\\mimikatz.exe", 146 | "C:\\bonza\\netcat.exe", 147 | "C\\Program Files (x86)\\Microsoft Office\\Office14\\WINWORD.EXE", 148 | selfImage, 149 | } 150 | commandLines := []string{ 151 | "", 152 | "", 153 | "", 154 | "/c echo [*]& pwd & echo [*]", 155 | "-ep bypass -window hidden -enc 'AAA='", 156 | "dpapi::masterkey", 157 | "-k netsvcs -p -s Schedule", 158 | "-c 'print(\"hyper\")'", 159 | "-lvp 8000", 160 | } 161 | users := []string{ 162 | "PATHDOWS\\PATH", 163 | "PATHDOWS\\DODGY", 164 | "NT AUTHORITY\\SYSTEM", 165 | "LocalService", 166 | "NetworkService", 167 | "Guest", 168 | } 169 | 170 | fmt.Printf("[f] Faking event generation\n") 171 | go func() { 172 | for { 173 | // Only fire events if there are players 174 | rand.Seed(time.Now().UnixNano()) 175 | if len(MinecraftPlayers) != 0 { 176 | image := getRandom(imageNames) 177 | parentImage := getRandom(imageNames) 178 | event := map[string]string{ 179 | "ProcessId": "-1", 180 | "Image": image, 181 | "CommandLine": fmt.Sprintf("%s %s", image, getRandom(commandLines)), 182 | "User": getRandom(users), 183 | "UtcTime": time.Now().Format("2006-01-02 15:04:05.000"), 184 | "ParentProcessId": "-1", 185 | "ParentImage": parentImage, 186 | "ParentCommandLine": fmt.Sprintf("%s %s", parentImage, getRandom(commandLines)), 187 | "Computer": "PATHDOWS", 188 | } 189 | rule := allRules[rand.Intn(len(allRules))] 190 | RaiseAlert(event, &rule, rule.Logsource.Category) 191 | } 192 | time.Sleep(time.Duration(rand.Intn(15)) * time.Second) 193 | } 194 | }() 195 | } 196 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/chicken.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "minecraft:chicken", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | "component_groups": { 11 | "minecraft:chicken_baby": { 12 | "minecraft:is_baby": { 13 | }, 14 | "minecraft:scale": { 15 | "value":0.5 16 | }, 17 | "minecraft:ageable": { 18 | "duration": 1200, 19 | "feed_items": [ 20 | "wheat_seeds", 21 | "beetroot_seeds", 22 | "melon_seeds", 23 | "pumpkin_seeds" 24 | ], 25 | "grow_up": { 26 | "event": "minecraft:ageable_grow_up", 27 | "target": "self" 28 | } 29 | }, 30 | "minecraft:behavior.follow_parent": { 31 | "priority": 5, 32 | "speed_multiplier": 1.1 33 | } 34 | }, 35 | 36 | "minecraft:chicken_adult": { 37 | "minecraft:experience_reward": { 38 | "on_bred": "Math.Random(1,7)", 39 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 40 | }, 41 | "minecraft:loot": { 42 | "table": "loot_tables/entities/chicken.json" 43 | }, 44 | "minecraft:breedable": { 45 | "require_tame": false, 46 | "breeds_with": { 47 | "mate_type": "minecraft:chicken", 48 | "baby_type": "minecraft:chicken", 49 | "breed_event": { 50 | "event": "minecraft:entity_born", 51 | "target": "baby" 52 | } 53 | }, 54 | "breed_items": [ 55 | "wheat_seeds", 56 | "beetroot_seeds", 57 | "melon_seeds", 58 | "pumpkin_seeds" 59 | ] 60 | }, 61 | "minecraft:behavior.breed": { 62 | "priority": 3, 63 | "speed_multiplier": 1.0 64 | }, 65 | "minecraft:rideable": { 66 | "seat_count": 1, 67 | "family_types": [ 68 | "zombie" 69 | ], 70 | "seats": { 71 | "position": [ 0.0, 0.4, 0.0 ] 72 | } 73 | }, 74 | "minecraft:spawn_entity": { 75 | "entities": { 76 | "min_wait_time": 300, 77 | "max_wait_time": 600, 78 | "spawn_sound": "plop", 79 | "spawn_item": "egg", 80 | "filters": { 81 | "test": "rider_count", "subject": "self", "operator": "==", "value": 0 82 | } 83 | } 84 | } 85 | } 86 | }, 87 | 88 | "components": { 89 | "minecraft:is_hidden_when_invisible": { 90 | }, 91 | "minecraft:type_family": { 92 | "family": [ "chicken", "mob" ] 93 | }, 94 | "minecraft:breathable": { 95 | "total_supply": 15, 96 | "suffocate_time": 0 97 | }, 98 | "minecraft:collision_box": { 99 | "width": 0.6, 100 | "height": 0.8 101 | }, 102 | "minecraft:nameable": { 103 | "alwaysShow": true 104 | }, 105 | "minecraft:health": { 106 | "value": 4, 107 | "max": 4 108 | }, 109 | "minecraft:hurt_on_condition": { 110 | "damage_conditions": [ 111 | { 112 | "filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true }, 113 | "cause": "lava", 114 | "damage_per_tick": 4 115 | } 116 | ] 117 | }, 118 | "minecraft:movement": { 119 | "value": 0.25 120 | }, 121 | "minecraft:damage_sensor": { 122 | "triggers": [ 123 | { 124 | "cause": "fall", 125 | "deals_damage": false 126 | }, 127 | { 128 | "on_damage": { 129 | "filters": { 130 | "all_of": [ 131 | { "test": "has_damage", "value": "fatal" }, 132 | { "test": "is_family", "subject": "other", "value": "player" }, 133 | { "none_of": { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} } 134 | ] 135 | }, 136 | "event": "special_death_event", 137 | "target": "self" 138 | } 139 | }, 140 | { 141 | "on_damage": { 142 | "filters": { 143 | "all_of": [ 144 | { "test": "has_damage", "value": "fatal" }, 145 | { "test": "is_family", "subject": "other", "value": "player" }, 146 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} 147 | ] 148 | }, 149 | "event": "special_death_event_diamond", 150 | "target": "self" 151 | } 152 | } 153 | ] 154 | }, 155 | "minecraft:leashable": { 156 | "soft_distance": 4.0, 157 | "hard_distance": 6.0, 158 | "max_distance": 10.0 159 | }, 160 | "minecraft:balloonable": { 161 | "mass": 0.5 162 | }, 163 | "minecraft:navigation.walk": { 164 | "can_path_over_water": true, 165 | "avoid_damage_blocks": true 166 | }, 167 | "minecraft:movement.basic": { 168 | }, 169 | "minecraft:jump.static": { 170 | }, 171 | "minecraft:can_climb": { 172 | }, 173 | "minecraft:despawn": { 174 | "despawn_from_distance": {} 175 | }, 176 | "minecraft:behavior.float": { 177 | "priority": 0 178 | }, 179 | "minecraft:behavior.panic": { 180 | "priority": 1, 181 | "speed_multiplier": 1.5 182 | }, 183 | "minecraft:behavior.mount_pathing": { 184 | "priority": 2, 185 | "speed_multiplier": 1.5, 186 | "target_dist": 0.0, 187 | "track_target": true 188 | }, 189 | "minecraft:behavior.tempt": { 190 | "priority": 4, 191 | "speed_multiplier": 1.0, 192 | "items": [ 193 | "wheat_seeds", 194 | "beetroot_seeds", 195 | "melon_seeds", 196 | "pumpkin_seeds" 197 | ] 198 | }, 199 | "minecraft:behavior.random_stroll": { 200 | "priority": 6, 201 | "speed_multiplier": 1.0 202 | }, 203 | "minecraft:behavior.look_at_player": { 204 | "priority": 7, 205 | "look_distance": 6.0, 206 | "probability": 0.02 207 | }, 208 | "minecraft:behavior.random_look_around": { 209 | "priority": 8 210 | }, 211 | "minecraft:physics": { 212 | }, 213 | "minecraft:pushable": { 214 | "is_pushable": true, 215 | "is_pushable_by_piston": true 216 | }, 217 | "minecraft:conditional_bandwidth_optimization": { 218 | } 219 | }, 220 | 221 | "events": { 222 | "special_death_event": { }, 223 | "special_death_event_diamond": { }, 224 | "from_egg": { 225 | "add": { "component_groups": [ "minecraft:chicken_baby" ] } 226 | }, 227 | 228 | "minecraft:entity_spawned": { 229 | "randomize": [ 230 | { 231 | "weight": 95, 232 | "trigger": "minecraft:spawn_adult" 233 | }, 234 | { 235 | "weight": 5, 236 | "add": { 237 | "component_groups": [ 238 | "minecraft:chicken_baby" 239 | ] 240 | } 241 | } 242 | ] 243 | }, 244 | 245 | "minecraft:entity_born": { 246 | "remove": { 247 | }, 248 | "add": { 249 | "component_groups": [ 250 | "minecraft:chicken_baby" 251 | ] 252 | } 253 | }, 254 | 255 | "minecraft:ageable_grow_up": { 256 | "remove": { 257 | "component_groups": [ 258 | "minecraft:chicken_baby" 259 | ] 260 | }, 261 | "add": { 262 | "component_groups": [ 263 | "minecraft:chicken_adult" 264 | ] 265 | } 266 | }, 267 | 268 | "minecraft:spawn_adult": { 269 | "add": { 270 | "component_groups": [ 271 | "minecraft:chicken_adult" 272 | ] 273 | } 274 | } 275 | } 276 | } 277 | } -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/cow.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "minecraft:cow", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | "component_groups": { 11 | "minecraft:cow_baby": { 12 | "minecraft:is_baby": { 13 | }, 14 | "minecraft:scale": { 15 | "value":0.5 16 | }, 17 | "minecraft:ageable": { 18 | "duration": 1200, 19 | "feed_items": "wheat", 20 | "grow_up": { 21 | "event": "minecraft:ageable_grow_up", 22 | "target": "self" 23 | } 24 | }, 25 | 26 | "minecraft:behavior.follow_parent": { 27 | "priority": 6, 28 | "speed_multiplier": 1.1 29 | } 30 | }, 31 | 32 | "minecraft:cow_adult": { 33 | "minecraft:experience_reward": { 34 | "on_bred": "Math.Random(1,7)", 35 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 36 | }, 37 | "minecraft:loot": { 38 | "table": "loot_tables/entities/cow.json" 39 | }, 40 | "minecraft:behavior.breed": { 41 | "priority": 3, 42 | "speed_multiplier": 1.0 43 | }, 44 | "minecraft:breedable": { 45 | "require_tame": false, 46 | "breed_items": "wheat", 47 | "breeds_with": { 48 | "mate_type": "minecraft:cow", 49 | "baby_type": "minecraft:cow", 50 | "breed_event": { 51 | "event": "minecraft:entity_born", 52 | "target": "baby" 53 | } 54 | } 55 | }, 56 | "minecraft:interact": { 57 | "interactions": [ 58 | { 59 | "on_interact": { 60 | "filters": { 61 | "all_of": [ 62 | { "test": "is_family", "subject" : "other", "value" : "player"}, 63 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "bucket:0"} 64 | ] 65 | } 66 | }, 67 | "use_item": true, 68 | "transform_to_item": "bucket:1", 69 | "play_sounds": "milk", 70 | "interact_text": "action.interact.milk" 71 | } 72 | ] 73 | } 74 | } 75 | }, 76 | 77 | "components": { 78 | "minecraft:damage_sensor": { 79 | "triggers": [ 80 | { 81 | "on_damage": { 82 | "filters": { 83 | "all_of": [ 84 | { "test": "has_damage", "value": "fatal" }, 85 | { "test": "is_family", "subject": "other", "value": "player" }, 86 | { "none_of": { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} } 87 | ] 88 | }, 89 | "event": "special_death_event", 90 | "target": "self" 91 | } 92 | }, 93 | { 94 | "on_damage": { 95 | "filters": { 96 | "all_of": [ 97 | { "test": "has_damage", "value": "fatal" }, 98 | { "test": "is_family", "subject": "other", "value": "player" }, 99 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} 100 | ] 101 | }, 102 | "event": "special_death_event_diamond", 103 | "target": "self" 104 | } 105 | } 106 | ] 107 | }, 108 | "minecraft:is_hidden_when_invisible": { 109 | }, 110 | "minecraft:type_family": { 111 | "family": [ "cow", "mob" ] 112 | }, 113 | "minecraft:breathable": { 114 | "total_supply": 15, 115 | "suffocate_time": 0 116 | }, 117 | "minecraft:navigation.walk": { 118 | "can_path_over_water": true, 119 | "avoid_water": true, 120 | "avoid_damage_blocks": true 121 | }, 122 | "minecraft:movement.basic": { 123 | 124 | }, 125 | "minecraft:jump.static": { 126 | }, 127 | "minecraft:can_climb": { 128 | }, 129 | "minecraft:collision_box": { 130 | "width": 0.9, 131 | "height": 1.3 132 | }, 133 | "minecraft:nameable": { 134 | "alwaysShow": true 135 | }, 136 | "minecraft:health": { 137 | "value": 10, 138 | "max": 10 139 | }, 140 | "minecraft:hurt_on_condition": { 141 | "damage_conditions": [ 142 | { 143 | "filters": { 144 | "test": "in_lava", 145 | "subject": "self", 146 | "operator": "==", 147 | "value": true 148 | }, 149 | "cause": "lava", 150 | "damage_per_tick": 4 151 | } 152 | ] 153 | }, 154 | "minecraft:movement": { 155 | "value": 0.25 156 | }, 157 | "minecraft:despawn": { 158 | "despawn_from_distance": {} 159 | }, 160 | "minecraft:behavior.float": { 161 | "priority": 0 162 | }, 163 | "minecraft:behavior.panic": { 164 | "priority": 1, 165 | "speed_multiplier": 1.25 166 | }, 167 | "minecraft:behavior.mount_pathing": { 168 | "priority": 2, 169 | "speed_multiplier": 1.5, 170 | "target_dist": 0.0, 171 | "track_target": true 172 | }, 173 | "minecraft:behavior.breed": { 174 | "priority": 3, 175 | "speed_multiplier": 1.0 176 | }, 177 | "minecraft:behavior.tempt": { 178 | "priority": 4, 179 | "speed_multiplier": 1.25, 180 | "items": [ 181 | "wheat" 182 | ] 183 | }, 184 | "minecraft:behavior.follow_parent": { 185 | "priority": 5, 186 | "speed_multiplier": 1.1 187 | }, 188 | "minecraft:behavior.random_stroll": { 189 | "priority": 6, 190 | "speed_multiplier": 0.8 191 | }, 192 | "minecraft:behavior.look_at_player": { 193 | "priority": 7, 194 | "look_distance": 6.0, 195 | "probability": 0.02 196 | }, 197 | "minecraft:behavior.random_look_around": { 198 | "priority": 9 199 | }, 200 | "minecraft:leashable": { 201 | "soft_distance": 4.0, 202 | "hard_distance": 6.0, 203 | "max_distance": 10.0 204 | }, 205 | "minecraft:balloonable": { 206 | }, 207 | "minecraft:rideable": { 208 | "seat_count": 1, 209 | "family_types": [ 210 | "zombie" 211 | ], 212 | "seats": { 213 | "position": [ 0.0, 1.105, 0.0 ] 214 | } 215 | }, 216 | "minecraft:physics": { 217 | }, 218 | "minecraft:pushable": { 219 | "is_pushable": true, 220 | "is_pushable_by_piston": true 221 | }, 222 | "minecraft:conditional_bandwidth_optimization": { 223 | } 224 | }, 225 | 226 | "events": { 227 | "special_death_event": { }, 228 | "special_death_event_diamond": { }, 229 | "minecraft:entity_spawned": { 230 | "randomize": [ 231 | { 232 | "weight": 95, 233 | "trigger": "minecraft:spawn_adult" 234 | }, 235 | { 236 | "weight": 5, 237 | "add": { 238 | "component_groups": [ 239 | "minecraft:cow_baby" 240 | ] 241 | } 242 | } 243 | ] 244 | }, 245 | 246 | "minecraft:entity_born": { 247 | "add": { 248 | "component_groups": [ 249 | "minecraft:cow_baby" 250 | ] 251 | } 252 | }, 253 | 254 | "minecraft:entity_transformed": { 255 | "remove": { 256 | }, 257 | "add": { 258 | "component_groups": [ 259 | "minecraft:cow_adult" 260 | ] 261 | } 262 | }, 263 | 264 | "minecraft:ageable_grow_up": { 265 | "remove": { 266 | "component_groups": [ 267 | "minecraft:cow_baby" 268 | ] 269 | }, 270 | "add": { 271 | "component_groups": [ 272 | "minecraft:cow_adult" 273 | ] 274 | } 275 | }, 276 | 277 | "minecraft:spawn_adult": { 278 | "add": { 279 | "component_groups": [ 280 | "minecraft:cow_adult" 281 | ] 282 | } 283 | } 284 | } 285 | } 286 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SIEMCraft logo](assets/siemcraft_logo.png) 2 | 3 | **SIEMCraft - Security Information and Event Management in Minecraft** 4 | 5 | (please do not take this serioussly, for more information read [this blog post](https://blog.tofile.dev/2022/06/10/siemcraft.html)) 6 | 7 | Inspired by [Kubecraftadmin](https://github.com/erjadi/kubecraftadmin), this project allows you to monitor 8 | and detect intrusions across your entire Windows domain, while still mining mad diamond. 9 | 10 | ![minecraft player looking at a pig with SIGMA rule information aboe it's head](assets/siemcraft_pig.png) 11 | 12 | Also see [this demo video](https://youtu.be/8Vyf8Y5wcRY) of SIEMCRAFT in VR. 13 | 14 | - [How it works](#how-it-works) 15 | - [Event Log collecter](#event-log-collecter) 16 | - [SIGMA Rule detection engine](#sigma-rule-detection-engine) 17 | - [Entity generator](#entity-generator) 18 | - [Player action responder](#player-action-responder) 19 | - [How it works - diagram](#how-it-works---diagram) 20 | - [Building](#building) 21 | - [Binary Controller](#binary-controller) 22 | - [Minecraft Addons](#minecraft-addons) 23 | - [Rules](#rules) 24 | - [Installing](#installing) 25 | - [Controller](#controller) 26 | - [Addons](#addons) 27 | - [Running](#running) 28 | - [Controller](#controller-1) 29 | - [Addons](#addons-1) 30 | - [Why would you make this?](#why-would-you-make-this) 31 | 32 | # How it works 33 | SIEMCRAFT is a project that combines a standalone executable 'controller', with a Minecraft addon, designed to 34 | enable a person to manage and respond to security alerts from within Minecraft. The project has a 35 | number of elements: 36 | ## Event Log collecter 37 | Using [RawSec's Win32 library](https://github.com/0xrawsec/golang-win32), SIEMCraft subscribes to 38 | various Windows Event logs, to collect events from 39 | - Microsoft Sysmon 40 | - ETW (via [Sealighter](https://github.com/pathtofile/Sealighter)) 41 | - Security, System, and Applicaiton Event logs 42 | 43 | Using Windows Event Forwarding (WEF), you can have SIEMCRAFT run from the central machine and collect 44 | event from an entire Windows Domain 45 | 46 | ## SIGMA Rule detection engine 47 | SIEMCraft will then run events through a user-supplied list of [SIGMA](https://github.com/SigmaHQ/sigma) detection rules 48 | using [Bradley Kemp's library](https://github.com/bradleyjkemp/sigma-go), to detect supsicious and malicious activity within the raw events. Using SigmaHQ's ruleset is also supported 49 | 50 | ## Entity generator 51 | If a rule detects suspicious behaviour, it will trigger the creation of new entity within a person's Minecraft server, 52 | nearby to the player. This entity will display information about: 53 | - The name of the rule triggered 54 | - The Machine name the rule was triggered on 55 | - The user responsible for the process that triggered the rule 56 | - The Image, CommandLine, and PID of the Process 57 | - The Image and PID of the Parent Process 58 | - Other relevant information 59 | 60 | Different types of entities are created depending on the detection severity: 61 | - Low: Chicken 62 | - Medium: Pig or Cow 63 | - High: [Spider, Panda, or Bear](https://adversary.crowdstrike.com/en-US/adversary/cozy-bear/) 64 | 65 | ## Player action responder 66 | If the entity is killed by a player weilding a `Diamond Sword`, SIEMCRAFT will then kill either the process or the parent process, so long as the process image is one of 67 | - cmd.exe 68 | - pwsh.exe 69 | - powershell.exe 70 | - wword.exe 71 | 72 | If the entity is killed by any other means the event is silently dismissed. 73 | 74 | # How it works - diagram 75 | ![incredibly overcomplicated diagram of the above overview](/assets/diagram.png) 76 | 77 | 78 | # Building 79 | You can grab pre-built artefacts from the [releases](https://github.com/pathtofile/siemcraft/releases) page. 80 | 81 | Otherwise, there are two parts to build: 82 | ## Binary Controller 83 | ```bash 84 | go build -o siemcraft.exe ./src 85 | ``` 86 | 87 | ## Minecraft Addons 88 | There are three Minecraft addons, a 'behaviour' pack and an 'entity' pack. Packs are just ZIPs, and can be combined 89 | into a single `.mcaddon` ZIP for extra portability: 90 | ```powershell 91 | # Windows 92 | Compress-Archive -Path "siemcraft_addon_behavior" -DestinationPath "siemcraft_addon_behavior.mcpack" -Force 93 | Compress-Archive -Path "siemcraft_addon_resource" -DestinationPath "siemcraft_addon_resource.mcpack" -Force 94 | Compress-Archive -Path "siemcraft_addon_gametest" -DestinationPath "siemcraft_addon_gametest.mcpack" -Force 95 | Compress-Archive -Path "*.mcpack -DestinationPath" "siemcraft.mcaddon" -Force 96 | 97 | # Linux 98 | zip -r siemcraft_addon_behavior.mcpack siemcraft_addon_behavior 99 | zip -r siemcraft_addon_resource.mcpack siemcraft_addon_resource 100 | zip -r siemcraft_addon_gametest.mcpack siemcraft_addon_gametest 101 | zip -r siemcraft_addon_resource.mcaddon *.mcpack 102 | ``` 103 | 104 | ## Rules 105 | You will also need some SIGMA rules for SIEMCRAFT to comapre raw events to. 106 | Either use the ones in this repository's [rules directory](rules/), or use 107 | [SIGMA's community rules](https://github.com/SigmaHQ/sigma). Note not all of these rules work with SIEMCRAFT 108 | (see [this](https://github.com/bradleyjkemp/sigma-go/issues/9) discussion). 109 | 110 | # Installing 111 | ## Controller 112 | Place the siemcraft binary anywhere on the machine where the event logs are being generated (usually the same 113 | machine as minecraft). 114 | 115 | ## Addons 116 | To install the Minecraft addon, double-click on the `.mcpack` from the machine with the Minecraft client. 117 | This should install all packs, which you can confirm by clicking `Settings` in Minecraft: 118 | ![Minecraft settings with Pack installed](assets/siemcraft_settings.png) 119 | 120 | # Running 121 | ## Controller 122 | Start the SIEMCRAFT controller binary from an elevated prompt, giving it the path to the folder containing the SIGMA rules: 123 | ```powershell 124 | $> siemcraft.exe --rules .\rules 125 | [r] Parsing SIGMA rules from: .\rules 126 | [r] Found rule: Security Logon 127 | [r] Found rule: Where Execution 128 | [r] Found rule: Whoami Execution 129 | [r] Number of rules found: 3 130 | [e] Starting event subscription 131 | [e] Subscribing to Event Log channels: 132 | [e] - Microsoft-Windows-Sysmon/Operational 133 | [e] - Security 134 | [m] starting SIEMCraft, run this command to connect: 135 | /connect 127.0.0.1:8000/ws 136 | ``` 137 | 138 | Siemcraft accepts the following commandline options: 139 | ``` 140 | -bind string 141 | Address to bind websocket to (default "127.0.0.1") 142 | -channels value 143 | Comma-seperated list of event logs to subscribe to 144 | (default ["Microsoft-Windows-Sysmon/Operational", "Security"]) 145 | -fakeEvents 146 | Don't subscript to event logs, just fake generate them 147 | -noKill 148 | Never attempt to kill a process 149 | -port string 150 | Port to bind websocket to (default "8000") 151 | -rules string 152 | Folder containing SIGMA rules (default ".\\rules") 153 | ``` 154 | 155 | ## Addons 156 | First, if running SIEMCRAFT on the same local host as the Minecraft client, you need to allow Minecraft to 157 | talk to your local network. Run this in an elevated PowerShell: 158 | ```powershell 159 | # First find the "Package Family Name" of Minecraft, which should look like like 'microsoft.minecraftuwp_RANDOM_STRING' 160 | $family_name=(Get-AppxPackage '*minecraft*').PackageFamilyName 161 | 162 | # Enable minecraft to reach loopback network 163 | cmd.exe /C "checknetisolation loopbackexempt -a -n=$family_name" 164 | 165 | # Check it worked, you should see minecraft here 166 | checknetisolation loopbackexempt -s 167 | ``` 168 | 169 | Next, create a new Minecraft world with the following options: 170 | - All cheats and experiments enabled (including GameTest), and achievements turned off 171 | - All the SIEMCRAFT 'Resource' and 'Behaviour' packs activated 172 | 173 | ![Siemcraft options, all experiments on](assets/siemcraft_settings2.png) 174 | ![Siemcraft addons enbled](assets/siemcraft_settings3.png) 175 | 176 | Once the Map is created, open up the console and type this command to connect 177 | to the SIEMCRAFT controller 178 | ``` 179 | /connect :/ws 180 | ``` 181 | 182 | By default the IP Address and port are: 183 | ``` 184 | /connect 127.0.0.1:8000/ws 185 | ``` 186 | 187 | ![Entering SIEMCRAFt command](assets/siemcraft_command.png) 188 | 189 | You should see positive output in both the Minecraft UI and in the Controller's output. 190 | 191 | # Why would you make this? 192 | See [the blog post](https://blog.tofile.dev/2022/06/10/siemcraft.html), but tl;dr is because I'm an idiot who was bored. 193 | I also presented this "work" at a local security meetup, you can see the slides [here](/assets/diamond_talk_sharable.pdf) 194 | (but the blog has more info, and the talk wasn't recorded). 195 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/bear.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "siemcraft:bear", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | 11 | "component_groups": { 12 | "minecraft:baby": { 13 | "minecraft:is_baby": { 14 | }, 15 | "minecraft:scale": { 16 | "value": 0.5 17 | }, 18 | "minecraft:ageable": { 19 | "duration": 1200, 20 | "grow_up": { 21 | "event": "minecraft:ageable_grow_up", 22 | "target": "self" 23 | } 24 | }, 25 | 26 | "minecraft:behavior.follow_parent": { 27 | "priority": 4, 28 | "speed_multiplier": 1.25 29 | } 30 | }, 31 | 32 | "minecraft:baby_wild": { 33 | "minecraft:on_target_acquired": { 34 | "event": "minecraft:on_scared", 35 | "target": "self" 36 | }, 37 | "minecraft:behavior.nearest_attackable_target": { 38 | "priority": 4, 39 | "entity_types": [ 40 | { 41 | "filters": { "test" : "is_family", "subject" : "other", "value" : "player"}, 42 | "max_dist": 16 43 | } 44 | ] 45 | } 46 | }, 47 | 48 | "minecraft:baby_scared": { 49 | "minecraft:angry": { 50 | "duration": 1, 51 | "broadcast_anger": true, 52 | "broadcast_range": 41, 53 | "calm_event": { 54 | "event": "minecraft:baby_on_calm", 55 | "target": "self" 56 | } 57 | } 58 | }, 59 | 60 | "minecraft:adult": { 61 | "minecraft:experience_reward": { 62 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 63 | }, 64 | "minecraft:loot": { 65 | "table": "loot_tables/entities/polar_bear.json" 66 | } 67 | }, 68 | 69 | "minecraft:adult_wild": { 70 | "minecraft:on_target_acquired": { 71 | "event": "minecraft:on_anger", 72 | "target": "self" 73 | }, 74 | "minecraft:on_friendly_anger": { 75 | "event": "minecraft:on_anger", 76 | "target": "self" 77 | }, 78 | "minecraft:behavior.nearest_attackable_target": { 79 | "priority": 4, 80 | "entity_types": [ 81 | { 82 | "filters": { 83 | "test": "is_family", 84 | "subject": "other", 85 | "value": "player" 86 | }, 87 | "max_dist": 16 88 | } 89 | ], 90 | "must_see": false 91 | } 92 | }, 93 | 94 | "minecraft:adult_hostile": { 95 | "minecraft:attack": { 96 | "damage": 6.0 97 | }, 98 | "minecraft:angry": { 99 | "duration": 500, 100 | "broadcast_anger": false, 101 | "broadcast_range": 20, 102 | "calm_event": { 103 | "event": "minecraft:on_calm", 104 | "target": "self" 105 | } 106 | }, 107 | 108 | "minecraft:behavior.stomp_attack": { 109 | "priority": 1, 110 | "track_target": true, 111 | "require_complete_path": true, 112 | "stomp_range_multiplier": 2.0, 113 | "no_damage_range_multiplier": 2.0 114 | } 115 | } 116 | }, 117 | 118 | "components": { 119 | "minecraft:damage_sensor": { 120 | "triggers": [ 121 | { 122 | "on_damage": { 123 | "filters": { 124 | "all_of": [ 125 | { "test": "has_damage", "value": "fatal" }, 126 | { "test": "is_family", "subject": "other", "value": "player" } 127 | ] 128 | }, 129 | "event": "special_death_event", 130 | "target": "self" 131 | } 132 | } 133 | ] 134 | }, 135 | "minecraft:is_hidden_when_invisible": { 136 | }, 137 | "minecraft:type_family": { 138 | "family": [ "polarbear", "bear", "mob" ] 139 | }, 140 | "minecraft:breathable": { 141 | "total_supply": 15, 142 | "suffocate_time": 0 143 | }, 144 | "minecraft:nameable": { 145 | "alwaysShow": true 146 | }, 147 | "minecraft:health": { 148 | "value": 30 149 | }, 150 | "minecraft:hurt_on_condition": { 151 | "damage_conditions": [ 152 | { 153 | "filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true }, 154 | "cause": "lava", 155 | "damage_per_tick": 4 156 | } 157 | ] 158 | }, 159 | "minecraft:collision_box": { 160 | "width": 1.3, 161 | "height": 1.4 162 | }, 163 | "minecraft:movement": { 164 | "value": 0.25 165 | }, 166 | "minecraft:water_movement": { 167 | "drag_factor": 0.98 168 | }, 169 | "minecraft:navigation.walk": { 170 | "can_path_over_water": true, 171 | "avoid_damage_blocks": true 172 | }, 173 | "minecraft:movement.basic": { 174 | 175 | }, 176 | "minecraft:jump.static": { 177 | }, 178 | "minecraft:can_climb": { 179 | }, 180 | "minecraft:follow_range": { 181 | "value": 48 182 | }, 183 | "minecraft:despawn": { 184 | "despawn_from_distance": {} 185 | }, 186 | "minecraft:behavior.float": { 187 | "priority": 0 188 | }, 189 | "minecraft:behavior.hurt_by_target": { 190 | "priority": 1 191 | }, 192 | "minecraft:behavior.random_stroll": { 193 | "priority": 5 194 | }, 195 | "minecraft:behavior.look_at_player": { 196 | "priority": 6, 197 | "target_distance": 6.0, 198 | "probability": 0.02 199 | }, 200 | "minecraft:behavior.random_look_around": { 201 | "priority": 7 202 | }, 203 | "minecraft:physics": { 204 | }, 205 | "minecraft:pushable": { 206 | "is_pushable": true, 207 | "is_pushable_by_piston": true 208 | }, 209 | "minecraft:behavior.panic": { 210 | "priority": 2, 211 | "speed_multiplier": 2.0 212 | }, 213 | "minecraft:leashable": { 214 | "soft_distance": 4.0, 215 | "hard_distance": 6.0, 216 | "max_distance": 10.0 217 | }, 218 | "minecraft:conditional_bandwidth_optimization": { 219 | } 220 | }, 221 | 222 | "events": { 223 | "special_death_event": { }, 224 | "special_death_event_diamond": { }, 225 | "minecraft:entity_spawned": { 226 | "randomize": [ 227 | { 228 | "weight": 9, 229 | "add": { 230 | "component_groups": [ 231 | "minecraft:adult", 232 | "minecraft:adult_wild", 233 | "minecraft:adult_hostile" 234 | ] 235 | } 236 | }, 237 | { 238 | "weight": 1, 239 | "add": { 240 | "component_groups": [ 241 | "minecraft:baby", 242 | "minecraft:baby_wild" 243 | ] 244 | } 245 | } 246 | ] 247 | }, 248 | 249 | "minecraft:entity_born": { 250 | "add": { 251 | "component_groups": [ 252 | "minecraft:baby", 253 | "minecraft:baby_wild" 254 | ] 255 | } 256 | }, 257 | 258 | "minecraft:ageable_grow_up": { 259 | "remove": { 260 | "component_groups": [ 261 | "minecraft:baby", 262 | "minecraft:baby_wild", 263 | "minecraft:baby_scared" 264 | ] 265 | }, 266 | "add": { 267 | "component_groups": [ 268 | "minecraft:adult", 269 | "minecraft:adult_wild", 270 | "minecraft:adult_hostile" 271 | ] 272 | } 273 | }, 274 | 275 | "minecraft:on_calm": { 276 | }, 277 | 278 | "minecraft:on_anger": { 279 | "remove": { 280 | "component_groups": [ 281 | "minecraft:adult_wild" 282 | ] 283 | }, 284 | "add": { 285 | "component_groups": [ 286 | "minecraft:adult_hostile" 287 | ] 288 | } 289 | }, 290 | 291 | "minecraft:baby_on_calm": { 292 | "remove": { 293 | "component_groups": [ 294 | "minecraft:baby_scared" 295 | ] 296 | }, 297 | "add": { 298 | "component_groups": [ 299 | "minecraft:baby_wild" 300 | ] 301 | } 302 | }, 303 | 304 | "minecraft:on_scared": { 305 | "remove": { 306 | "component_groups": [ 307 | "minecraft:baby_wild" 308 | ] 309 | }, 310 | "add": { 311 | "component_groups": [ 312 | "minecraft:baby_scared" 313 | ] 314 | } 315 | } 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/pig.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "minecraft:pig", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | "component_groups": { 11 | "minecraft:pig_baby": { 12 | "minecraft:is_baby": { 13 | }, 14 | "minecraft:scale": { 15 | "value": 0.5 16 | }, 17 | "minecraft:ageable": { 18 | "duration": 1200, 19 | "feed_items": [ "carrot", "beetroot", "potato" ], 20 | "grow_up": { 21 | "event": "minecraft:ageable_grow_up", 22 | "target": "self" 23 | } 24 | }, 25 | 26 | "minecraft:behavior.follow_parent": { 27 | "priority": 6, 28 | "speed_multiplier": 1.1 29 | } 30 | }, 31 | 32 | "minecraft:pig_transform": { 33 | "minecraft:transformation": { 34 | "into": "minecraft:pig_zombie", 35 | "delay": 0.5 36 | } 37 | }, 38 | 39 | "minecraft:pig_adult": { 40 | "minecraft:experience_reward": { 41 | "on_bred": "Math.Random(1,7)", 42 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 43 | }, 44 | "minecraft:loot": { 45 | "table": "loot_tables/entities/pig.json" 46 | }, 47 | "minecraft:behavior.breed": { 48 | "priority": 4, 49 | "speed_multiplier": 1.0 50 | }, 51 | "minecraft:breedable": { 52 | "require_tame": false, 53 | "breeds_with": { 54 | "mate_type": "minecraft:pig", 55 | "baby_type": "minecraft:pig", 56 | "breed_event": { 57 | "event": "minecraft:entity_born", 58 | "target": "baby" 59 | } 60 | }, 61 | "breed_items": [ "carrot", "beetroot", "potato" ] 62 | } 63 | }, 64 | 65 | "minecraft:pig_unsaddled": { 66 | "minecraft:interact": { 67 | "interactions": [ 68 | { 69 | "on_interact": { 70 | "filters": { "test": "has_equipment", "subject": "other", "domain": "hand", "value": "saddle"}, 71 | "event": "minecraft:on_saddled" 72 | }, 73 | "use_item": true, 74 | "play_sounds": "saddle", 75 | "interact_text": "action.interact.saddle" 76 | } 77 | ] 78 | }, 79 | "minecraft:rideable": { 80 | "seat_count": 1, 81 | "family_types": [ 82 | "zombie" 83 | ], 84 | "seats": { 85 | "position": [ 0.0, 0.63, 0.0 ] 86 | } 87 | } 88 | }, 89 | 90 | "minecraft:pig_saddled": { 91 | "minecraft:is_saddled": { 92 | }, 93 | "minecraft:loot": { 94 | "table": "loot_tables/entities/pig_saddled.json" 95 | }, 96 | "minecraft:boostable": { 97 | "speed_multiplier": 2.0, 98 | "duration": 3.0, 99 | "boost_items": [ 100 | { 101 | "item": "carrotOnAStick", 102 | "damage": 2, 103 | "replace_item": "fishing_rod" 104 | } 105 | ] 106 | }, 107 | "minecraft:rideable": { 108 | "seat_count": 1, 109 | "interact_text": "action.interact.mount", 110 | "family_types": [ 111 | "player" 112 | ], 113 | "seats": { 114 | "position": [ 0.0, 0.63, 0.0 ] 115 | } 116 | }, 117 | "minecraft:item_controllable": { 118 | "control_items": "carrotOnAStick" 119 | }, 120 | "minecraft:behavior.controlled_by_player": { 121 | "priority": 0 122 | } 123 | } 124 | }, 125 | 126 | 127 | "components": { 128 | "minecraft:is_hidden_when_invisible": { 129 | }, 130 | "minecraft:damage_sensor": { 131 | "triggers": [ 132 | { 133 | "on_damage": { 134 | "filters": [ 135 | { 136 | "test": "is_family", 137 | "subject": "other", 138 | "value": "lightning" 139 | }, 140 | { 141 | "test": "is_difficulty", 142 | "operator": "!=", 143 | "value": "peaceful" 144 | } 145 | ], 146 | "event": "become_zombie" 147 | }, 148 | "deals_damage": false 149 | }, 150 | { 151 | "on_damage": { 152 | "filters": { 153 | "all_of": [ 154 | { "test": "has_damage", "value": "fatal" }, 155 | { "test": "is_family", "subject": "other", "value": "player" }, 156 | { "none_of": { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} } 157 | ] 158 | }, 159 | "event": "special_death_event", 160 | "target": "self" 161 | } 162 | }, 163 | { 164 | "on_damage": { 165 | "filters": { 166 | "all_of": [ 167 | { "test": "has_damage", "value": "fatal" }, 168 | { "test": "is_family", "subject": "other", "value": "player" }, 169 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} 170 | ] 171 | }, 172 | "event": "special_death_event_diamond", 173 | "target": "self" 174 | } 175 | } 176 | ] 177 | }, 178 | "minecraft:type_family": { 179 | "family": [ "pig", "mob" ] 180 | }, 181 | "minecraft:breathable": { 182 | "total_supply": 15, 183 | "suffocate_time": 0 184 | }, 185 | "minecraft:nameable": { 186 | "alwaysShow": true 187 | }, 188 | "minecraft:health": { 189 | "value": 10, 190 | "max": 10 191 | }, 192 | "minecraft:hurt_on_condition": { 193 | "damage_conditions": [ 194 | { 195 | "filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true }, 196 | "cause": "lava", 197 | "damage_per_tick": 4 198 | } 199 | ] 200 | }, 201 | "minecraft:movement": { 202 | "value": 0.25 203 | }, 204 | "minecraft:navigation.walk": { 205 | "can_path_over_water": true, 206 | "avoid_water": true, 207 | "avoid_damage_blocks": true 208 | }, 209 | "minecraft:movement.basic": { 210 | 211 | }, 212 | "minecraft:jump.static": { 213 | }, 214 | "minecraft:can_climb": { 215 | }, 216 | "minecraft:collision_box": { 217 | "width": 0.9, 218 | "height": 0.9 219 | }, 220 | "minecraft:leashable": { 221 | "soft_distance": 4.0, 222 | "hard_distance": 6.0, 223 | "max_distance": 10.0 224 | }, 225 | "minecraft:balloonable": { 226 | "mass": 0.75 227 | }, 228 | "minecraft:despawn": { 229 | "despawn_from_distance": {} 230 | }, 231 | "minecraft:behavior.mount_pathing": { 232 | "priority": 1, 233 | "speed_multiplier": 1.25, 234 | "target_dist": 0.0, 235 | "track_target": true 236 | }, 237 | "minecraft:behavior.float": { 238 | "priority": 2 239 | }, 240 | "minecraft:behavior.panic": { 241 | "priority": 3, 242 | "speed_multiplier": 1.25 243 | }, 244 | "minecraft:behavior.tempt": { 245 | "priority": 5, 246 | "speed_multiplier": 1.2, 247 | "items": [ 248 | "potato", 249 | "carrot", 250 | "beetroot", 251 | "carrotOnAStick" 252 | ] 253 | }, 254 | "minecraft:behavior.random_stroll": { 255 | "priority": 7, 256 | "speed_multiplier": 1.0 257 | }, 258 | "minecraft:behavior.look_at_player": { 259 | "priority": 8, 260 | "look_distance": 6.0, 261 | "probability": 0.02 262 | }, 263 | "minecraft:behavior.random_look_around": { 264 | "priority": 9 265 | }, 266 | "minecraft:physics": { 267 | }, 268 | "minecraft:pushable": { 269 | "is_pushable": true, 270 | "is_pushable_by_piston": true 271 | }, 272 | "minecraft:conditional_bandwidth_optimization": { 273 | } 274 | }, 275 | 276 | 277 | "events": { 278 | "special_death_event": { }, 279 | "special_death_event_diamond": { }, 280 | "become_zombie": { 281 | "remove": { 282 | }, 283 | "add": { 284 | "component_groups": [ 285 | "minecraft:pig_transform" 286 | ] 287 | } 288 | }, 289 | "minecraft:entity_spawned": { 290 | "randomize": [ 291 | { 292 | "weight": 95, 293 | "trigger": "minecraft:spawn_adult" 294 | }, 295 | { 296 | "weight": 5, 297 | "remove": { 298 | }, 299 | "add": { 300 | "component_groups": [ 301 | "minecraft:pig_baby" 302 | ] 303 | 304 | } 305 | } 306 | ] 307 | }, 308 | 309 | "minecraft:entity_born": { 310 | "remove": { 311 | }, 312 | "add": { 313 | "component_groups": [ 314 | "minecraft:pig_baby" 315 | ] 316 | } 317 | }, 318 | 319 | "minecraft:ageable_grow_up": { 320 | "remove": { 321 | "component_groups": [ 322 | "minecraft:pig_baby" 323 | ] 324 | }, 325 | "add": { 326 | "component_groups": [ 327 | "minecraft:pig_adult", 328 | "minecraft:pig_unsaddled" 329 | ] 330 | } 331 | }, 332 | 333 | "minecraft:on_saddled": { 334 | "remove": { 335 | "component_groups": [ 336 | "minecraft:pig_unsaddled" 337 | ] 338 | }, 339 | "add": { 340 | "component_groups": [ 341 | "minecraft:pig_saddled" 342 | ] 343 | } 344 | }, 345 | "minecraft:spawn_adult": { 346 | "add": { 347 | "component_groups": [ 348 | "minecraft:pig_adult", 349 | "minecraft:pig_unsaddled" 350 | ] 351 | } 352 | } 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/minecraft.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "math/rand" 10 | "os" 11 | "os/exec" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | "golang.org/x/sync/errgroup" 17 | 18 | "github.com/bradleyjkemp/sigma-go" 19 | "github.com/pathtofile/mcwss" 20 | "github.com/pathtofile/mcwss/protocol/event" 21 | ) 22 | 23 | var MinecraftPlayers []*mcwss.Player 24 | var neverKillProcesses bool 25 | var hostname string 26 | 27 | // Used to tag messages from and to MineCraft addon 28 | const messagePrefix = "[SIEMCRAFT]" 29 | 30 | // Sent from the add-on when an entity is killed 31 | type SIEMCraftMessage struct { 32 | // Item used to kill the entity 33 | Item string `json:"item"` 34 | // If a Projectile was used, it'd be here 35 | Projectile string `json:"projectile"` 36 | // The saved event 37 | EventB64 string `json:"eventb64"` 38 | } 39 | 40 | func getRandom(slice []string) string { 41 | return slice[rand.Intn(len(slice))] 42 | } 43 | 44 | func RaiseAlert(event map[string]string, rule *sigma.Rule, channel string) { 45 | // If there's not a player don't do anything 46 | if len(MinecraftPlayers) == 0 { 47 | return 48 | } 49 | fmt.Printf("[m] Rule Hit: %s\n", rule.Title) 50 | 51 | for _, p := range MinecraftPlayers { 52 | actionbar(p, fmt.Sprintf("Rule Hit: %s", rule.Title)) 53 | } 54 | 55 | // Seed randomness for entity spawning 56 | rand.Seed(time.Now().UnixNano()) 57 | 58 | // Select random player to spawn near 59 | player := MinecraftPlayers[rand.Intn(len(MinecraftPlayers))] 60 | 61 | name := "" 62 | jsonTag := "" 63 | if channel == "sysmon" { 64 | // Only display certain feilds 65 | simpleFields := []string{ 66 | // Common 67 | "Computer", 68 | "ProcessId", 69 | "Image", 70 | "CommandLine", 71 | "User", 72 | // "UtcTime", 73 | // Process 74 | "ParentProcessId", 75 | "ParentImage", 76 | // "ParentCommandLine", 77 | // Network connection 78 | "SourceIp", 79 | "SourcePort", 80 | "DestinationIp", 81 | "DestinationPort", 82 | // Image and Driver Load 83 | "ImageLoaded", 84 | // File Create 85 | "TargetFilename", 86 | // Registry 87 | "TargetObject", 88 | // DNS 89 | "QueryName", 90 | "QueryStatus", 91 | "QueryResults", 92 | } 93 | simpleEvent := map[string]string{} 94 | for _, field := range simpleFields { 95 | if event[field] != "" { 96 | simpleEvent[field] = strings.ReplaceAll(event[field], "\n", "") 97 | } 98 | } 99 | eventData, _ := json.MarshalIndent(simpleEvent, "", " ") 100 | name = string(eventData) 101 | 102 | // Now store event as a base64 JSON in the entity's tag 103 | evantDataJSON, _ := json.Marshal(simpleEvent) 104 | jsonTag = base64.StdEncoding.EncodeToString(evantDataJSON) 105 | } else { 106 | // For everything else, print full event because I cbf custom parsing everything 107 | eventData, _ := json.MarshalIndent(event, "", " ") 108 | name = string(eventData) 109 | 110 | // Now store event as a base64 JSON in the entity's tag 111 | evantDataJSON, _ := json.Marshal(event) 112 | jsonTag = base64.StdEncoding.EncodeToString(evantDataJSON) 113 | } 114 | 115 | // Remove or escape characters that can't be easily displayed in minecraft 116 | name = strings.ReplaceAll(name, "\\", "\\\\") 117 | name = strings.ReplaceAll(name, "{", "") 118 | name = strings.ReplaceAll(name, "}", "") 119 | name = strings.ReplaceAll(name, "\"", "") 120 | 121 | // Add Rule header 122 | name = fmt.Sprintf("Rule: %s\n----------\n%s", rule.Title, name) 123 | 124 | // The event severity determins the entity spawned 125 | entity := "" 126 | if rule.Level == "low" { 127 | entity = "chicken" 128 | } else if rule.Level == "medium" { 129 | entity = getRandom([]string{"pig", "cow"}) 130 | } else if rule.Level == "high" { 131 | entity = getRandom([]string{"spider", "panda", "siemcraft:bear"}) 132 | } 133 | 134 | // Generate locations and spawn command 135 | xPos := rand.Intn(18) + 2 136 | zPos := rand.Intn(18) + 2 137 | if rand.Float32() < 0.5 { 138 | xPos = xPos * -1 139 | } 140 | if rand.Float32() < 0.5 { 141 | zPos = zPos * -1 142 | } 143 | fmt.Printf("[m] Spawning %s at %d:%d from player %s\n", entity, xPos, zPos, player.Name()) 144 | spawnCmd := fmt.Sprintf("summon %s \"%s\" ~%d ~ ~%d", entity, name, xPos, zPos) 145 | player.Exec(spawnCmd, nil) 146 | 147 | // Add the event as JSON in a tag 148 | // This command selects any un-tagged entity within a small radious of where we jsut summoned it 149 | // (in case it was spawned in the air and has started to fall) 150 | // We have to do these tag shenanigans to select the target, as we can't get a multi-line name 151 | // And we can't rename after the fact (so can't 'summon as simpleName->tag->rename to multiline') 152 | jsonTag = fmt.Sprintf("%s%s", messagePrefix, jsonTag) 153 | tagCmd := fmt.Sprintf("tag @e[tag=,type=%s,x=~%d,y=~,z=~%d,r=50,rm=0] add \"%s\"", entity, xPos, zPos, jsonTag) 154 | player.Exec(tagCmd, nil) 155 | 156 | // Teleport to a safe place, as they may have spawned into the wall 157 | spreadCmd := fmt.Sprintf("spreadplayers ~%d ~%d 0 5 @e[tag=\"%s\"]", xPos, zPos, jsonTag) 158 | player.Exec(spreadCmd, nil) 159 | } 160 | 161 | // actionbar will display a message to the player 162 | func actionbar(player *mcwss.Player, message string) { 163 | player.Exec(fmt.Sprintf("title %s actionbar %s", player.Name(), message), nil) 164 | } 165 | 166 | func onMessage(event *event.PlayerMessage) { 167 | if event.Sender != "Script Engine" || event.Type != "tell" { 168 | return 169 | } 170 | // First trim quotes, then check and trim prefix 171 | msgString := event.Message[1 : len(event.Message)-1] 172 | if !strings.HasPrefix(msgString, messagePrefix) { 173 | return 174 | } 175 | msgString = msgString[len(messagePrefix):] 176 | 177 | // De-serialise from JSON 178 | message := SIEMCraftMessage{} 179 | err := json.Unmarshal([]byte(msgString), &message) 180 | if err != nil { 181 | // Not our message 182 | fmt.Printf("[m] Not our message? %s\n", err.Error()) 183 | return 184 | } 185 | // Decode event from base64 186 | eventString, err := base64.StdEncoding.DecodeString(message.EventB64) 187 | if err != nil { 188 | fmt.Printf("[m] Failed to decode Evnet from message: %s\n", err.Error()) 189 | return 190 | } 191 | messageEvent := map[string]string{} 192 | err = json.Unmarshal([]byte(eventString), &messageEvent) 193 | if err != nil { 194 | fmt.Printf("[m] Failed to unmarshal event: %s\n", err.Error()) 195 | return 196 | } 197 | 198 | // Check Item used to kill, determine if we're killing process 199 | // if neverKillProcesses is set, then also never attempt to kill it 200 | // Also check event is from this machine and not a remote one (i.e. a WEF forwarded event) 201 | if !neverKillProcesses && messageEvent["Computer"] == hostname && strings.Contains(message.Item, "diamond_sword") { 202 | // Diamond weapon, kill a process! 203 | 204 | // kill if image is one of these 205 | killableImages := []string{"cmd.exe", "pwsh.exe", "powershell.exe", "wword.exe"} 206 | pidToKill := 0 207 | for _, killableImage := range killableImages { 208 | if strings.HasSuffix(messageEvent["Image"], killableImage) { 209 | pidToKill, _ = strconv.Atoi(messageEvent["ProcessId"]) 210 | fmt.Printf("[*] Killing Pid %d (%s)\n", pidToKill, messageEvent["CommandLine"]) 211 | } 212 | } 213 | if pidToKill == 0 { 214 | // Check parent, maybe we can kill it? 215 | for _, killableImage := range killableImages { 216 | if strings.HasSuffix(messageEvent["ParentImage"], killableImage) { 217 | pidToKill, _ = strconv.Atoi(messageEvent["ParentProcessId"]) 218 | fmt.Printf("[*] Killing Parent Pid %d (%s)\n", pidToKill, messageEvent["ParentCommandLine"]) 219 | } 220 | } 221 | } 222 | // Pid could be < 0 in testing, and 0 if there was no process ID found 223 | if pidToKill <= 0 { 224 | fmt.Printf("[m] Event handled\n") 225 | } else { 226 | process, err := os.FindProcess(pidToKill) 227 | if err != nil { 228 | fmt.Printf("[*] Failed to find process %d: %s\n", pidToKill, err.Error()) 229 | return 230 | } 231 | err = process.Kill() 232 | if err != nil { 233 | fmt.Printf("[*] Failed to kill process %d: %s\n", pidToKill, err.Error()) 234 | return 235 | } 236 | } 237 | } else { 238 | fmt.Printf("[m] Event handled for PID %s\n", messageEvent["ProcessId"]) 239 | } 240 | } 241 | 242 | func onConnection(player *mcwss.Player) { 243 | playerName := player.Name() 244 | fmt.Printf("[m] Player %s has connected\n", playerName) 245 | 246 | // Send welcome message 247 | player.Exec(fmt.Sprintf("title %s title SIEMCraft", playerName), nil) 248 | 249 | // Provide player with equipment 250 | player.Exec("give @s diamond_sword", nil) 251 | player.Exec("give @s netherite_sword", nil) 252 | 253 | // TODO: remove? 254 | player.Exec("time set noon", nil) 255 | player.Exec("weather clear", nil) 256 | player.Exec("alwaysday", nil) 257 | 258 | // Register to recieve messages, we'll 259 | // use them to get data back from minecraft addon 260 | player.OnPlayerMessage(onMessage) 261 | 262 | // Add player 263 | MinecraftPlayers = append(MinecraftPlayers, player) 264 | } 265 | 266 | func onDisconnection(player *mcwss.Player) { 267 | // Log player disconnect 268 | // Find and remove player from list 269 | for i, p := range MinecraftPlayers { 270 | if p == player { 271 | fmt.Printf("[m] Main player %s has disconnected\n", player.Name()) 272 | MinecraftPlayers = append(MinecraftPlayers[:i], MinecraftPlayers[i+1:]...) 273 | break 274 | } 275 | } 276 | } 277 | 278 | func setHostName() error { 279 | // Gotta do this the dodgy way 280 | cmd := exec.Command("powershell", "-c", `[System.Net.Dns]::GetHostByName($env:computerName).HostName`) 281 | var out bytes.Buffer 282 | cmd.Stdout = &out 283 | err := cmd.Run() 284 | if err != nil { 285 | return err 286 | } 287 | hostname = strings.TrimSpace(out.String()) 288 | return nil 289 | } 290 | 291 | func StartMinecraftWebsocket(bindAddress string, bindPort string, noKill bool) error { 292 | MinecraftPlayers = make([]*mcwss.Player, 0, 5) 293 | 294 | // Create a new server using the default configuration. To use specific configuration, pass a *wss.Config{} in here. 295 | address := fmt.Sprintf("%s:%s", bindAddress, bindPort) 296 | var c = mcwss.Config{HandlerPattern: "/ws", Address: address} 297 | server := mcwss.NewServer(&c) 298 | if server == nil { 299 | return errors.New("failed to create minecraft server config") 300 | } 301 | 302 | neverKillProcesses = noKill 303 | err := setHostName() 304 | if err != nil { 305 | return err 306 | } 307 | 308 | server.OnConnection(onConnection) 309 | server.OnDisconnection(onDisconnection) 310 | 311 | // Start websocket server 312 | fmt.Println("[m] starting SIEMCraft, run this command to connect:") 313 | fmt.Printf(" /connect %s/ws\n", address) 314 | var errorGroup errgroup.Group 315 | errorGroup.Go(func() error { 316 | return server.Run() 317 | }) 318 | 319 | // Run forever (or until ctrl+c) 320 | return errorGroup.Wait() 321 | } 322 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/spider.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "minecraft:spider", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | 11 | 12 | "component_groups": { 13 | "minecraft:spider_jockey": { 14 | "minecraft:addrider": { 15 | "entity_type": "minecraft:skeleton" 16 | }, 17 | "minecraft:rideable": { 18 | "seat_count": 1, 19 | "family_types": [ 20 | "skeleton" 21 | ], 22 | "seats": { 23 | "position": [ 0.0, 0.54, 0.0 ] 24 | } 25 | } 26 | }, 27 | 28 | "minecraft:spider_stray_jockey": { 29 | "minecraft:addrider": { 30 | "entity_type": "minecraft:skeleton.stray" 31 | }, 32 | "minecraft:rideable": { 33 | "seat_count": 1, 34 | "family_types": [ 35 | "skeleton" 36 | ], 37 | "seats": { 38 | "position": [ 0.0, 0.54, 0.0 ] 39 | } 40 | } 41 | }, 42 | 43 | "minecraft:spider_wither_jockey": { 44 | "minecraft:addrider": { 45 | "entity_type": "minecraft:skeleton.wither" 46 | }, 47 | "minecraft:rideable": { 48 | "seat_count": 1, 49 | "family_types": [ 50 | "skeleton" 51 | ], 52 | "seats": { 53 | "position": [ 0.0, 0.54, 0.0 ] 54 | } 55 | } 56 | }, 57 | 58 | "minecraft:spider_neutral": { 59 | "minecraft:environment_sensor": { 60 | "triggers": { 61 | "filters": { 62 | "test": "is_brightness", 63 | "operator": "<", 64 | "value": 0.49 65 | }, 66 | "event": "minecraft:become_hostile" 67 | } 68 | }, 69 | "minecraft:on_target_acquired": { 70 | "event": "minecraft:become_angry" 71 | } 72 | }, 73 | 74 | "minecraft:spider_hostile": { 75 | "minecraft:on_target_acquired": { 76 | "event": "minecraft:become_angry" 77 | }, 78 | "minecraft:behavior.nearest_attackable_target": { 79 | "priority": 2, 80 | "must_see": true, 81 | "attack_interval": 5, 82 | "entity_types": [ 83 | { 84 | "filters": { 85 | "any_of": [ 86 | { 87 | "test": "is_family", 88 | "subject": "other", 89 | "value": "player" 90 | }, 91 | { 92 | "test": "is_family", 93 | "subject": "other", 94 | "value": "snowgolem" 95 | }, 96 | { 97 | "test": "is_family", 98 | "subject": "other", 99 | "value": "irongolem" 100 | } 101 | ] 102 | }, 103 | "max_dist": 16 104 | } 105 | ] 106 | } 107 | }, 108 | 109 | "minecraft:spider_angry": { 110 | "minecraft:angry": { 111 | "duration": 10, 112 | "duration_delta": 3, 113 | "calm_event": { 114 | "event": "minecraft:become_calm", 115 | "target": "self" 116 | } 117 | }, 118 | "minecraft:behavior.leap_at_target": { 119 | "priority": 4, 120 | "yd": 0.4, 121 | "must_be_on_ground": false 122 | }, 123 | "minecraft:behavior.melee_attack": { 124 | "priority": 3, 125 | "track_target": true, 126 | "reach_multiplier": 0.8 127 | } 128 | } 129 | }, 130 | 131 | "components": { 132 | "minecraft:damage_sensor": { 133 | "triggers": [ 134 | { 135 | "on_damage": { 136 | "filters": { 137 | "all_of": [ 138 | { "test": "has_damage", "value": "fatal" }, 139 | { "test": "is_family", "subject": "other", "value": "player" }, 140 | { "none_of": { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} } 141 | ] 142 | }, 143 | "event": "special_death_event", 144 | "target": "self" 145 | } 146 | }, 147 | { 148 | "on_damage": { 149 | "filters": { 150 | "all_of": [ 151 | { "test": "has_damage", "value": "fatal" }, 152 | { "test": "is_family", "subject": "other", "value": "player" }, 153 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} 154 | ] 155 | }, 156 | "event": "special_death_event_diamond", 157 | "target": "self" 158 | } 159 | } 160 | ] 161 | }, 162 | "minecraft:is_hidden_when_invisible": { 163 | }, 164 | "minecraft:experience_reward": { 165 | "on_death": "query.last_hit_by_player ? 5 : 0" 166 | }, 167 | "minecraft:nameable": { 168 | "alwaysShow": true 169 | }, 170 | "minecraft:type_family": { 171 | "family": [ "spider", "monster", "mob", "arthropod" ] 172 | }, 173 | "minecraft:breathable": { 174 | "total_supply": 15, 175 | "suffocate_time": 0 176 | }, 177 | "minecraft:loot": { 178 | "table": "loot_tables/entities/spider.json" 179 | }, 180 | "minecraft:collision_box": { 181 | "width": 1.4, 182 | "height": 0.9 183 | }, 184 | "minecraft:health": { 185 | "value": 16, 186 | "max": 16 187 | }, 188 | "minecraft:hurt_on_condition": { 189 | "damage_conditions": [ 190 | { 191 | "filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true }, 192 | "cause": "lava", 193 | "damage_per_tick": 4 194 | } 195 | ] 196 | }, 197 | "minecraft:movement": { 198 | "value": 0.3 199 | }, 200 | "minecraft:navigation.climb": { 201 | "can_path_over_water": true 202 | }, 203 | "minecraft:movement.basic": { 204 | }, 205 | "minecraft:jump.static": { 206 | }, 207 | "minecraft:can_climb": { 208 | }, 209 | "minecraft:attack": { 210 | "damage": 3 211 | }, 212 | "minecraft:despawn": { 213 | "despawn_from_distance": {} 214 | }, 215 | "minecraft:behavior.float": { 216 | "priority": 1 217 | }, 218 | "minecraft:behavior.mount_pathing": { 219 | "priority": 5, 220 | "speed_multiplier": 1.25, 221 | "target_dist": 0.0, 222 | "track_target": true 223 | }, 224 | "minecraft:behavior.random_stroll": { 225 | "priority": 6, 226 | "speed_multiplier": 0.8 227 | }, 228 | "minecraft:behavior.look_at_player": { 229 | "priority": 7, 230 | "look_distance": 6.0, 231 | "probability": 0.02 232 | }, 233 | "minecraft:behavior.random_look_around": { 234 | "priority": 7 235 | }, 236 | "minecraft:behavior.hurt_by_target": { 237 | "priority": 1 238 | }, 239 | "minecraft:rideable": { 240 | "seat_count": 1, 241 | "family_types": [ 242 | "zombie" 243 | ], 244 | "seats": { 245 | "position": [ 0.0, 0.54, -0.1 ] 246 | } 247 | }, 248 | "minecraft:physics": { 249 | }, 250 | "minecraft:pushable": { 251 | "is_pushable": true, 252 | "is_pushable_by_piston": true 253 | }, 254 | "minecraft:conditional_bandwidth_optimization": { 255 | } 256 | }, 257 | 258 | "events": { 259 | "special_death_event": { }, 260 | "special_death_event_diamond": { }, 261 | "minecraft:entity_spawned": { 262 | "randomize": [ 263 | { 264 | "weight": 1, 265 | "randomize": [ 266 | { 267 | "weight": 80, 268 | "filters": { 269 | "all_of": [ 270 | { 271 | "test": "is_daytime", 272 | "value": false 273 | }, 274 | { 275 | "test": "is_snow_covered", 276 | "value": true 277 | }, 278 | { 279 | "test": "is_underground", 280 | "value": false 281 | } 282 | ] 283 | }, 284 | "remove": {}, 285 | "add": { 286 | "component_groups": [ 287 | "minecraft:spider_stray_jockey", 288 | "minecraft:spider_hostile", 289 | "minecraft:spider_angry" 290 | ] 291 | } 292 | }, 293 | { 294 | "weight": 80, 295 | "filters": { 296 | "test": "is_biome", 297 | "value": "the_nether" 298 | }, 299 | "remove": {}, 300 | "add": { 301 | "component_groups": [ 302 | "minecraft:spider_wither_jockey", 303 | "minecraft:spider_hostile", 304 | "minecraft:spider_angry" 305 | ] 306 | } 307 | }, 308 | { 309 | "weight": 20, 310 | "filters": { 311 | "any_of": [ 312 | { 313 | "test": "is_daytime", 314 | "value": false 315 | }, 316 | { 317 | "test": "is_underground", 318 | "value": true 319 | } 320 | ] 321 | }, 322 | "remove": {}, 323 | "add": { 324 | "component_groups": [ 325 | "minecraft:spider_jockey", 326 | "minecraft:spider_hostile", 327 | "minecraft:spider_angry" 328 | ] 329 | } 330 | } 331 | ] 332 | }, 333 | { 334 | "weight": 99, 335 | "remove": {}, 336 | "add": { 337 | "component_groups": [ 338 | "minecraft:spider_hostile", 339 | "minecraft:spider_angry" 340 | ] 341 | } 342 | } 343 | ] 344 | }, 345 | 346 | "minecraft:become_hostile": { 347 | "remove": { 348 | "component_groups": [ 349 | "minecraft:spider_neutral" 350 | ] 351 | }, 352 | "add": { 353 | "component_groups": [ 354 | "minecraft:spider_hostile" 355 | ] 356 | } 357 | }, 358 | 359 | "minecraft:become_neutral": { 360 | "remove": { 361 | "component_groups": [ 362 | "minecraft:spider_hostile" 363 | ] 364 | }, 365 | "add": { 366 | "component_groups": [ 367 | "minecraft:spider_neutral" 368 | ] 369 | } 370 | }, 371 | 372 | "minecraft:become_angry": { 373 | "add": { 374 | "component_groups": [ 375 | "minecraft:spider_angry" 376 | ] 377 | } 378 | }, 379 | "minecraft:become_calm": { 380 | } 381 | } 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/0xrawsec/golang-utils v1.1.8 h1:9TzAKzC7+V2IXaV/3Y1aXiUFHeShL3BXcfL6BheAEH0= 2 | github.com/0xrawsec/golang-utils v1.1.8/go.mod h1:DADTtCFY10qXjWmUVhhJqQIZdSweaHH4soYUDEi8mj0= 3 | github.com/0xrawsec/golang-win32 v1.0.10 h1:2/+XrkpOEa9WndZtAWBnvwi+d+paGc5bRWruJx91fmQ= 4 | github.com/0xrawsec/golang-win32 v1.0.10/go.mod h1:XBEh/JhPosCBiqLCoJsyyKF/+EdwbLaGxQOCsqGEMnQ= 5 | github.com/PaesslerAG/gval v1.0.0 h1:GEKnRwkWDdf9dOmKcNrar9EA1bz1z9DqPIO1+iLzhd8= 6 | github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= 7 | github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= 8 | github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk= 9 | github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= 10 | github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs= 11 | github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= 12 | github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= 13 | github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs= 14 | github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= 15 | github.com/bradleyjkemp/sigma-go v0.2.8 h1:bMU0BVgMlT4rYEuPUJKWe+KhKiM+wgLUhj/oNE9wH6A= 16 | github.com/bradleyjkemp/sigma-go v0.2.8/go.mod h1:eDT0OXQD6U68UQXw4XXaRz38Lb/PwJ/BKVCaPQ0I5fA= 17 | github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 20 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 22 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 23 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 24 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 27 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 28 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 29 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 30 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 31 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 32 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 33 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 34 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 35 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 36 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 37 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 38 | github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= 39 | github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 40 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= 41 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 42 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 43 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 44 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 45 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 46 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 47 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 48 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 49 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 50 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 51 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 52 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 53 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 54 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 55 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 56 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 57 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 58 | github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= 59 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 60 | github.com/pathtofile/mcwss v1.2.2 h1:/ycn7jynzvXYKkuJnMpCB7BnFtnpxF2ATle9ApH2EXs= 61 | github.com/pathtofile/mcwss v1.2.2/go.mod h1:96gBP5tbFM6LpSGZB3TzFxiptbrIAo5E23Dl+tJ8wqc= 62 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 63 | github.com/pkg/sftp v1.10.0/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk= 64 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 65 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 66 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 67 | github.com/sanity-io/litter v1.3.0/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= 68 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 69 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 70 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 71 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 72 | github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 73 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 74 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 75 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 76 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 77 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 78 | github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= 79 | github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= 80 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= 81 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= 82 | github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= 83 | github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 84 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 85 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 86 | golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 87 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 88 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 89 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 90 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 91 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 92 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 93 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 94 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 95 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 96 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= 97 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 98 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 99 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 101 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 102 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 103 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 104 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 105 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 106 | golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 107 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 108 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 109 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 110 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 113 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 114 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 115 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 116 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= 117 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 118 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 119 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 120 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 121 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 122 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 123 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 124 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 125 | golang.org/x/tools v0.0.0-20190320215829-36c10c0a621f/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 126 | golang.org/x/tools v0.0.0-20190625160430-252024b82959/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 127 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 128 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 129 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 130 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 131 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 132 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 133 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 134 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 135 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 136 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 137 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 138 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 139 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 140 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 141 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 142 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 143 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 144 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 145 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 146 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 147 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 148 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 149 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 150 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 151 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 152 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 153 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 154 | -------------------------------------------------------------------------------- /siemcraft_addon_behavior/entities/panda.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "minecraft:panda", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | "component_groups": { 11 | "minecraft:panda_baby": { 12 | "minecraft:is_baby": { 13 | }, 14 | "minecraft:scale": { 15 | "value": 0.4 16 | }, 17 | "minecraft:ageable": { 18 | "duration": 1200, 19 | "feed_items": "bamboo", 20 | "grow_up": { 21 | "event": "minecraft:ageable_grow_up", 22 | "target": "self" 23 | } 24 | }, 25 | "minecraft:behavior.roll": { 26 | "priority": 12, 27 | "probability": 0.0016 28 | }, 29 | "minecraft:behavior.follow_parent": { 30 | "priority": 13, 31 | "speed_multiplier": 1.1 32 | }, 33 | "minecraft:on_target_acquired": { 34 | "event": "minecraft:on_scared", 35 | "target": "self" 36 | }, 37 | "minecraft:behavior.sneeze": { 38 | "priority": 7, 39 | "probability": 0.0001666, 40 | "cooldown_time": 1.0, 41 | "within_radius": 10.0, 42 | "entity_types": [ 43 | { 44 | "filters": { 45 | "all_of": [ 46 | { 47 | "test": "has_component", 48 | "subject": "other", 49 | "operator": "!=", 50 | "value": "minecraft:is_baby" 51 | }, 52 | { 53 | "test": "is_family", 54 | "subject": "other", 55 | "value": "panda" 56 | }, 57 | { 58 | "test": "in_water", 59 | "subject": "other", 60 | "operator": "!=", 61 | "value": true 62 | }, 63 | { 64 | "test": "on_ground", 65 | "operator": "==", 66 | "value": true 67 | } 68 | ] 69 | }, 70 | "max_dist": 10 71 | } 72 | ], 73 | "drop_item_chance": 0.001, 74 | "loot_table": "loot_tables/entities/panda_sneeze.json", 75 | "prepare_sound": "presneeze", 76 | "prepare_time": 1.0, 77 | "sound": "sneeze" 78 | } 79 | }, 80 | "minecraft:panda_adult": { 81 | "minecraft:experience_reward": { 82 | "on_bred": "Math.Random(1,7)", 83 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 84 | }, 85 | "minecraft:loot": { 86 | "table": "loot_tables/entities/panda.json" 87 | }, 88 | "minecraft:behavior.breed": { 89 | "priority": 3, 90 | "speed_multiplier": 1.0 91 | }, 92 | "minecraft:breedable": { 93 | "require_tame": false, 94 | "blend_attributes": false, 95 | "environment_requirements": { 96 | "blocks": "bamboo", 97 | "count": 8, 98 | "radius": 5 99 | }, 100 | "breed_items": "bamboo", 101 | "breeds_with": { 102 | "mate_type": "minecraft:panda", 103 | "baby_type": "minecraft:panda", 104 | "breed_event": { 105 | "event": "minecraft:entity_born", 106 | "target": "baby" 107 | } 108 | }, 109 | "mutation_factor": { 110 | "variant": 1.0 111 | } 112 | }, 113 | "minecraft:on_target_acquired": { 114 | "event": "minecraft:become_angry", 115 | "target": "self" 116 | }, 117 | "minecraft:on_target_escape": { 118 | "event": "minecraft:on_calm", 119 | "target": "self" 120 | }, 121 | "minecraft:attack": { 122 | "damage": 2.0 123 | }, 124 | "minecraft:behavior.melee_attack": { 125 | "priority": 2, 126 | "track_target": true, 127 | "reach_multiplier": 1.0 128 | }, 129 | "minecraft:behavior.leap_at_target": { 130 | "priority": 4, 131 | "yd": 0.4, 132 | "must_be_on_ground": false 133 | }, 134 | "minecraft:angry": { 135 | "duration": 10, 136 | "duration_delta": 3, 137 | "calm_event": { 138 | "event": "minecraft:become_calm", 139 | "target": "self" 140 | } 141 | }, 142 | "minecraft:behavior.nearest_attackable_target": { 143 | "priority": 2, 144 | "must_see": true, 145 | "attack_interval": 5, 146 | "entity_types": [ 147 | { 148 | "filters": { 149 | "any_of": [ 150 | { 151 | "test": "is_family", 152 | "subject": "other", 153 | "value": "player" 154 | } 155 | ] 156 | }, 157 | "max_dist": 16 158 | } 159 | ] 160 | } 161 | }, 162 | "minecraft:panda_lazy": { 163 | "minecraft:variant": { 164 | "value": 1 165 | }, 166 | "minecraft:behavior.lay_down": { 167 | "priority": 5, 168 | "interval": 400, 169 | "random_stop_interval": 2000 170 | }, 171 | "minecraft:behavior.random_sitting": { 172 | "priority": 6, 173 | "start_chance": 0.02, 174 | "stop_chance": 0.2, 175 | "cooldown": 25, 176 | "min_sit_time": 15 177 | }, 178 | "minecraft:behavior.snacking": { 179 | "priority": 3, 180 | "snacking_cooldown": 17.5, 181 | "snacking_cooldown_min": 10, 182 | "snacking_stop_chance": 0.0011, 183 | "items": [ 184 | "bamboo", 185 | "cake" 186 | ] 187 | }, 188 | "minecraft:behavior.panic": { 189 | "priority": 1, 190 | // Lazy panda's move half the speed, so double their multiplier when they panic 191 | // Otherwise, they don't actually look like they're panicking 192 | "speed_multiplier": 2.5 193 | }, 194 | "minecraft:movement": { 195 | "value": 0.07 196 | } 197 | }, 198 | "minecraft:panda_worried": { 199 | "minecraft:variant": { 200 | "value": 2 201 | }, 202 | "minecraft:behavior.scared": { 203 | "priority": 1, 204 | "sound_interval": 20 205 | }, 206 | "minecraft:behavior.avoid_mob_type": { 207 | "priority": 5, 208 | "max_dist": 16, 209 | "max_flee": 20, 210 | "entity_types": [ 211 | { 212 | "filters": { 213 | "test": "is_family", 214 | "operator": "!=", 215 | "subject": "other", 216 | "value": "panda" 217 | }, 218 | "max_dist": 16, 219 | "sprint_speed_multiplier": 1.5 220 | } 221 | ] 222 | } 223 | }, 224 | "minecraft:panda_playful": { 225 | "minecraft:variant": { 226 | "value": 3 227 | }, 228 | "minecraft:behavior.roll": { 229 | "priority": 12, 230 | "probability": 0.013 231 | } 232 | }, 233 | "minecraft:panda_brown": { 234 | "minecraft:variant": { 235 | "value": 4 236 | } 237 | }, 238 | "minecraft:panda_weak": { 239 | "minecraft:variant": { 240 | "value": 5 241 | }, 242 | "minecraft:health": { 243 | "value": 10, 244 | "max": 10 245 | } 246 | }, 247 | "minecraft:panda_sneezing": { 248 | "minecraft:behavior.sneeze": { 249 | "priority": 7, 250 | "probability": 0.002, 251 | "cooldown_time": 1.0, 252 | "within_radius": 10.0, 253 | "entity_types": [ 254 | { 255 | "filters": { 256 | "all_of": [ 257 | { 258 | "test": "has_component", 259 | "subject": "other", 260 | "operator": "!=", 261 | "value": "minecraft:is_baby" 262 | }, 263 | { 264 | "test": "is_family", 265 | "subject": "other", 266 | "value": "panda" 267 | }, 268 | { 269 | "test": "in_water", 270 | "subject": "other", 271 | "operator": "!=", 272 | "value": true 273 | }, 274 | { 275 | "test": "on_ground", 276 | "operator": "==", 277 | "value": true 278 | } 279 | ] 280 | }, 281 | "max_dist": 10 282 | } 283 | ], 284 | "drop_item_chance": 0.001, 285 | "loot_table": "loot_tables/entities/panda_sneeze.json", 286 | "prepare_sound": "presneeze", 287 | "prepare_time": 1.0, 288 | "sound": "sneeze" 289 | } 290 | }, 291 | "minecraft:panda_aggressive": { 292 | "minecraft:type_family": { 293 | "family": [ "panda" , "panda_aggressive", "mob" ] 294 | }, 295 | "minecraft:variant": { 296 | "value": 6 297 | }, 298 | "minecraft:attack": { 299 | "damage": 6.0 300 | }, 301 | "minecraft:behavior.melee_attack": { 302 | "priority": 2, 303 | "track_target": true, 304 | "reach_multiplier": 1.0 305 | }, 306 | "minecraft:on_friendly_anger": { 307 | "event": "minecraft:on_anger", 308 | "target": "self" 309 | }, 310 | "minecraft:behavior.panic": { 311 | "priority": 1, 312 | "speed_multiplier": 1.25, 313 | // Aggressive pandas don't panic from mob damage 314 | "ignore_mob_damage": true 315 | } 316 | }, 317 | "minecraft:panda_angry": { 318 | "minecraft:angry": { 319 | "duration": 500, 320 | "broadcast_anger": true, 321 | "broadcast_range": 41, 322 | "broadcast_filters": { 323 | "test": "is_family", 324 | "operator": "==", 325 | "value": "panda_aggressive" 326 | }, 327 | "calm_event": { 328 | "event": "minecraft:on_calm", 329 | "target": "self" 330 | } 331 | }, 332 | "minecraft:on_target_acquired": { 333 | } 334 | }, 335 | "minecraft:baby_scared": { 336 | "minecraft:angry": { 337 | "duration": 1, 338 | "broadcast_anger": true, 339 | "broadcast_range": 41, 340 | "broadcast_filters": { 341 | "test": "is_family", 342 | "operator": "==", 343 | "value": "panda_aggressive" 344 | }, 345 | "calm_event": { 346 | "event": "minecraft:baby_on_calm", 347 | "target": "self" 348 | } 349 | } 350 | } 351 | }, 352 | 353 | 354 | "components": { 355 | "minecraft:damage_sensor": { 356 | "triggers": [ 357 | { 358 | "on_damage": { 359 | "filters": { 360 | "all_of": [ 361 | { "test": "has_damage", "value": "fatal" }, 362 | { "test": "is_family", "subject": "other", "value": "player" }, 363 | { "none_of": { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} } 364 | ] 365 | }, 366 | "event": "special_death_event", 367 | "target": "self" 368 | } 369 | }, 370 | { 371 | "on_damage": { 372 | "filters": { 373 | "all_of": [ 374 | { "test": "has_damage", "value": "fatal" }, 375 | { "test": "is_family", "subject": "other", "value": "player" }, 376 | { "test": "has_equipment", "domain": "hand", "subject": "other", "value": "diamond_sword"} 377 | ] 378 | }, 379 | "event": "special_death_event_diamond", 380 | "target": "self" 381 | } 382 | } 383 | ] 384 | }, 385 | "minecraft:is_hidden_when_invisible": { 386 | }, 387 | "minecraft:type_family": { 388 | "family": [ "panda" ] 389 | }, 390 | "minecraft:breathable": { 391 | "total_supply": 15, 392 | "suffocate_time": 0 393 | }, 394 | "minecraft:navigation.walk": { 395 | "can_float": true, 396 | "avoid_water": true, 397 | "avoid_damage_blocks": true 398 | }, 399 | "minecraft:movement.basic": { 400 | }, 401 | "minecraft:scale": { 402 | "value": 1.0 403 | }, 404 | "minecraft:behavior.hurt_by_target": { 405 | "priority": 1 406 | }, 407 | "minecraft:giveable": { 408 | "triggers": { 409 | "cooldown": 3.0, 410 | "items": [ 411 | "bamboo", 412 | "cake" 413 | ], 414 | "on_give": { 415 | "event": "minecraft:on_calm", 416 | "target": "self" 417 | } 418 | } 419 | }, 420 | "minecraft:inventory": { 421 | "inventory_size": 1, 422 | "private": true 423 | }, 424 | "minecraft:jump.static": { 425 | }, 426 | "minecraft:can_climb": { 427 | }, 428 | "minecraft:collision_box": { 429 | "width": 1.7, 430 | "height": 1.5 431 | }, 432 | "minecraft:nameable": { 433 | "alwaysShow": true 434 | }, 435 | "minecraft:health": { 436 | "value": 20, 437 | "max": 20 438 | }, 439 | "minecraft:hurt_on_condition": { 440 | "damage_conditions": [ 441 | { 442 | "filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true }, 443 | "cause": "lava", 444 | "damage_per_tick": 4 445 | } 446 | ] 447 | }, 448 | "minecraft:movement": { 449 | "value": 0.15 450 | }, 451 | "minecraft:water_movement": { 452 | "drag_factor": 0.98 453 | }, 454 | "minecraft:despawn": { 455 | "despawn_from_distance": {} 456 | }, 457 | "minecraft:behavior.float": { 458 | "priority": 0 459 | }, 460 | "minecraft:behavior.random_sitting": { 461 | "priority": 5, 462 | "start_chance": 0.01, 463 | "stop_chance": 0.3, 464 | "cooldown": 30, 465 | "min_sit_time": 10 466 | }, 467 | "minecraft:behavior.snacking": { 468 | "priority": 2, 469 | "snacking_cooldown": 22.5, 470 | "snacking_cooldown_min": 20, 471 | "snacking_stop_chance": 0.001334, 472 | "items": [ 473 | "bamboo", 474 | "cake" 475 | ] 476 | }, 477 | "minecraft:behavior.mount_pathing": { 478 | "priority": 5, 479 | "speed_multiplier": 1.5, 480 | "target_dist": 0.0, 481 | "track_target": true 482 | }, 483 | "minecraft:behavior.breed": { 484 | "priority": 3, 485 | "speed_multiplier": 1.0 486 | }, 487 | "minecraft:behavior.tempt": { 488 | "priority": 4, 489 | "speed_multiplier": 1.25, 490 | "items": [ 491 | "bamboo" 492 | ] 493 | }, 494 | "minecraft:behavior.random_stroll": { 495 | "priority": 14, 496 | "speed_multiplier": 0.8 497 | }, 498 | "minecraft:behavior.look_at_player": { 499 | "priority": 8, 500 | "look_distance": 6.0, 501 | "probability": 0.02 502 | }, 503 | "minecraft:behavior.random_look_around": { 504 | "priority": 9 505 | }, 506 | "minecraft:behavior.panic": { 507 | "priority": 1, 508 | "speed_multiplier": 1.25 509 | }, 510 | "minecraft:balloonable": { 511 | "mass": 1.5 512 | }, 513 | "minecraft:rideable": { 514 | "seat_count": 1, 515 | "family_types": [ 516 | "zombie" 517 | ], 518 | "seats": { 519 | "position": [ 0.0, 1.105, 0.0 ] 520 | } 521 | }, 522 | "minecraft:physics": { 523 | }, 524 | "minecraft:pushable": { 525 | "is_pushable": true, 526 | "is_pushable_by_piston": true 527 | }, 528 | "minecraft:variant": { 529 | "value": 0 530 | }, 531 | "minecraft:conditional_bandwidth_optimization": { 532 | }, 533 | "minecraft:genetics": { 534 | "mutation_rate": 0.03125, 535 | "genes": [ 536 | { 537 | "name": "panda_variant", 538 | "allele_range": { 539 | "range_min": 0, 540 | "range_max": 15 541 | }, 542 | "genetic_variants": [ 543 | { 544 | "main_allele": 0, 545 | "birth_event": { 546 | "event": "minecraft:panda_lazy", 547 | "target": "self" 548 | } 549 | }, 550 | { 551 | "main_allele": 1, 552 | "birth_event": { 553 | "event": "minecraft:panda_worried", 554 | "target": "self" 555 | } 556 | }, 557 | { 558 | "main_allele": 2, 559 | "birth_event": { 560 | "event": "minecraft:panda_playful", 561 | "target": "self" 562 | } 563 | }, 564 | { 565 | "main_allele": 3, 566 | "birth_event": { 567 | "event": "minecraft:panda_aggressive", 568 | "target": "self" 569 | } 570 | }, 571 | { 572 | "both_allele": { 573 | "range_min": 4, 574 | "range_max": 7 575 | }, 576 | "birth_event": { 577 | "event": "minecraft:panda_weak", 578 | "target": "self" 579 | } 580 | }, 581 | { 582 | "both_allele": { 583 | "range_min": 8, 584 | "range_max": 9 585 | }, 586 | "birth_event": { 587 | "event": "minecraft:panda_brown", 588 | "target": "self" 589 | } 590 | } 591 | ] 592 | } 593 | ] 594 | } 595 | }, 596 | 597 | "events": { 598 | "special_death_event": { }, 599 | "special_death_event_diamond": { }, 600 | "minecraft:entity_spawned": { 601 | "randomize": [ 602 | { 603 | "weight": 95, 604 | "add": { 605 | "component_groups": [ 606 | "minecraft:panda_adult" 607 | ] 608 | } 609 | }, 610 | { 611 | "weight": 5, 612 | "add": { 613 | "component_groups": [ 614 | "minecraft:panda_baby" 615 | ] 616 | } 617 | } 618 | ] 619 | }, 620 | 621 | "minecraft:entity_born": { 622 | "add": { 623 | "component_groups": [ 624 | "minecraft:panda_baby" 625 | ] 626 | } 627 | }, 628 | 629 | "minecraft:ageable_grow_up": { 630 | "sequence": [ 631 | { 632 | "remove": { 633 | "component_groups": [ 634 | "minecraft:panda_baby" 635 | ] 636 | } 637 | }, 638 | { 639 | "add": { 640 | "component_groups": [ 641 | "minecraft:panda_adult" 642 | ] 643 | } 644 | }, 645 | { 646 | "filters": { 647 | "test": "is_variant", 648 | "subject": "self", 649 | "operator": "==", 650 | "value": 3 651 | }, 652 | "add": { 653 | "component_groups": [ 654 | "minecraft:panda_playful" 655 | ] 656 | } 657 | }, 658 | { 659 | "filters": { 660 | "test": "is_variant", 661 | "subject": "self", 662 | "operator": "==", 663 | "value": 6 664 | }, 665 | "add": { 666 | "component_groups": [ 667 | "minecraft:panda_aggressive" 668 | ] 669 | } 670 | } 671 | ] 672 | }, 673 | "minecraft:panda_lazy": { 674 | "add": { 675 | "component_groups": [ 676 | "minecraft:panda_lazy" 677 | ] 678 | } 679 | }, 680 | "minecraft:panda_worried": { 681 | "add": { 682 | "component_groups": [ 683 | "minecraft:panda_worried" 684 | ] 685 | } 686 | }, 687 | "minecraft:panda_playful": { 688 | "add": { 689 | "component_groups": [ 690 | "minecraft:panda_playful" 691 | ] 692 | } 693 | }, 694 | "minecraft:panda_brown": { 695 | "add": { 696 | "component_groups": [ 697 | "minecraft:panda_brown" 698 | ] 699 | } 700 | }, 701 | "minecraft:panda_weak": { 702 | "sequence": [ 703 | { 704 | "add": { 705 | "component_groups": [ 706 | "minecraft:panda_weak" 707 | ] 708 | } 709 | }, 710 | { 711 | "filters": { 712 | "test": "has_component", 713 | "operator": "==", 714 | "value": "minecraft:is_baby" 715 | }, 716 | "add": { 717 | "component_groups": [ 718 | "minecraft:panda_sneezing" 719 | ] 720 | } 721 | } 722 | ] 723 | }, 724 | "minecraft:panda_aggressive": { 725 | "add": { 726 | "component_groups": [ 727 | "minecraft:panda_aggressive" 728 | ] 729 | } 730 | }, 731 | "minecraft:on_scared": { 732 | "add": { 733 | "component_groups": [ 734 | "minecraft:baby_scared" 735 | ] 736 | } 737 | }, 738 | "minecraft:baby_on_calm": { 739 | "remove": { 740 | "component_groups": [ 741 | "minecraft:baby_scared" 742 | ] 743 | } 744 | }, 745 | "minecraft:become_angry": { 746 | "add": { 747 | "component_groups": [ 748 | "minecraft:panda_angry" 749 | ] 750 | } 751 | }, 752 | "minecraft:on_calm": { 753 | } 754 | } 755 | } 756 | } --------------------------------------------------------------------------------