├── .gitignore ├── Makefile ├── anvil.c ├── anvil.h ├── bigtest.nbt ├── docs ├── build.html ├── images │ ├── build_example.jpg │ ├── cactus_republic.jpg │ ├── checkerboard.jpg │ ├── mani_stacked.jpg │ ├── mani_torches.jpg │ ├── mani_tower.jpg │ ├── mani_trim.jpg │ ├── melon_castle.jpg │ ├── pixelart.jpg │ ├── scan_after.jpg │ ├── scan_pos.jpg │ ├── server_list.png │ ├── space_valley.jpg │ ├── stairs1.jpg │ ├── stairs2.jpg │ ├── stairs3.jpg │ └── wrath_outpost.jpg ├── index.html ├── install.html ├── interface.html ├── intro.html └── styles.css ├── dumpreg.c ├── entity.c ├── entity.h ├── helpers.c ├── helpers.h ├── hud.c ├── hud.h ├── hudfonts.png ├── mapper.c ├── mcp_arg.c ├── mcp_arg.h ├── mcp_bplan.c ├── mcp_bplan.h ├── mcp_build.c ├── mcp_build.h ├── mcp_game.c ├── mcp_game.h ├── mcp_gamestate.c ├── mcp_gamestate.h ├── mcp_ids.c ├── mcp_ids.h ├── mcp_packet.c ├── mcp_packet.h ├── mcp_types.c ├── mcp_types.h ├── mcpdump.c ├── mcproxy.c ├── nbt.c ├── nbt.h ├── qholder.c ├── schematic └── kizhi_pogost.schematic ├── scripts ├── mcb_start └── mcb_update ├── slot.c ├── slot.h └── varint.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Files 2 | *.o 3 | *~ 4 | *.mcs 5 | bases.txt 6 | players.txt 7 | make.depend* 8 | 9 | # applications 10 | mcproxy 11 | mcpdump 12 | argtest 13 | varint 14 | nbttest 15 | 16 | # directories 17 | saved 18 | schematic 19 | bplan 20 | png 21 | csv 22 | projects 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g 2 | DEFS=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE 3 | INC=-I../libhelper 4 | LIBS_LIBHELPER=-L../libhelper -lhelper 5 | LIBS=$(LIBS_LIBHELPER) -lm -lpng -lz -lcurl -lcrypto -ljson-c -lresolv 6 | 7 | SRC_BASE=$(addsuffix .c, mcp_packet mcp_ids mcp_types nbt slot entity helpers) 8 | SRC_MCPROXY=$(addsuffix .c, mcproxy mcp_gamestate mcp_game mcp_build mcp_arg mcp_bplan hud) $(SRC_BASE) 9 | SRC_MCPDUMP=$(addsuffix .c, mcpdump mcp_gamestate anvil) $(SRC_BASE) 10 | SRC_QHOLDER=$(addsuffix .c, qholder) $(SRC_BASE) 11 | SRC_DUMPREG=$(addsuffix .c, dumpreg anvil) $(SRC_BASE) 12 | SRC_MAPPER=$(addsuffix .c, mapper) $(SRC_BASE) 13 | SRC_ALL=$(SRC_MCPROXY) mcpdump.c varint.c 14 | 15 | ALLBIN=mcproxy mcpdump varint qholder dumpreg mapper 16 | 17 | HDR_ALL=$(addsuffix .h, mcp_packet mcp_ids mcp_types nbt mcp_game mcp_gamestate mcp_build mcp_arg mcp_bplan slot entity) 18 | 19 | DEPFILE=make.depend 20 | 21 | ifeq ($(shell uname -s),SunOS) 22 | INC += -I$(HOME)/include 23 | LIBS += -lsocket -lnsl -lmd5 -L$(HOME)/lib 24 | CC = gcc 25 | else 26 | ifeq ($(shell uname -o),Cygwin) 27 | CFLAGS=-std=gnu99 28 | endif 29 | endif 30 | 31 | 32 | all: $(ALLBIN) 33 | 34 | mcproxy: $(SRC_MCPROXY:.c=.o) 35 | $(CC) -o $@ $^ $(LIBS) 36 | 37 | mcpdump: $(SRC_MCPDUMP:.c=.o) 38 | $(CC) -o $@ $^ $(LIBS) 39 | 40 | qholder: $(SRC_QHOLDER:.c=.o) 41 | $(CC) -o $@ $^ $(LIBS) 42 | 43 | dumpreg: $(SRC_DUMPREG:.c=.o) 44 | $(CC) -o $@ $^ $(LIBS) 45 | 46 | mapper: $(SRC_MAPPER:.c=.o) 47 | $(CC) -o $@ $^ $(LIBS) 48 | 49 | varint: varint.c 50 | $(CC) $(CFLAGS) $(INC) $(DEFS) -DTEST=1 -o $@ $^ $(LIBS) 51 | 52 | 53 | 54 | .c.o: $(DEPFILE) 55 | $(CC) $(CFLAGS) $(DEFS) $(INC) $(CONFIG) -o $@ -c $< 56 | 57 | $(DEPFILE): $(SRC_ALL) $(HDR_ALL) 58 | @rm -rf $(DEPFILE) $(DEPFILE).bak 59 | @touch $(DEPFILE) 60 | makedepend -Y -f $(DEPFILE) $(SRC_ALL) 2> /dev/null 61 | 62 | install: $(ALLBIN) 63 | @test -d $(HOME)/bin || mkdir $(HOME)/bin 64 | @cp -a scripts/* $(HOME)/bin 65 | @chmod +x $(HOME)/bin/mcb* 66 | @command -v mcb_update 2>&1 >> /dev/null || ( echo 'export PATH="$$HOME/bin:$$PATH"' >> ~/.bashrc ; echo "Please restart your shell or source ~/.bashrc to set PATH" ) 67 | 68 | clean: 69 | rm -f *.o *~ $(ALLBIN) $(TSTBIN) $(DEPFILE) 70 | 71 | FORCE: 72 | 73 | sinclude $(DEPFILE) 74 | -------------------------------------------------------------------------------- /anvil.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2016 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | /* 12 | anvil : handling of Anvil on-disk file format 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "anvil.h" 25 | 26 | // create an empty region 27 | mca * anvil_create() { 28 | lh_create_obj(mca, region); 29 | return region; 30 | } 31 | 32 | // free a region include all chunks 33 | void anvil_free(mca * region) { 34 | int i; 35 | for(i=0; idata[i]); 37 | lh_free(region); 38 | } 39 | 40 | // show allocated chunks of a region 41 | void anvil_dump(mca * region) { 42 | int i,j; 43 | for(i=0; idata[j] ? '#' : '.'); 46 | } 47 | printf("\n"); 48 | } 49 | } 50 | 51 | // load an anvil region from disk 52 | mca * anvil_load(const char *path) { 53 | uint8_t *buf; 54 | ssize_t sz = lh_load_alloc(path, &buf); 55 | if (sz<=0) return NULL; 56 | 57 | lh_create_obj(mca, region); 58 | 59 | int i; 60 | uint8_t *p = buf; 61 | uint8_t *t = buf+4096; 62 | for(i=0; its[i] = lh_read_int_be(t); 65 | if (choff) { // this chunk is non-empty 66 | ssize_t clen = (choff&0xff)<<12; 67 | lh_alloc_buf(region->data[i], clen); 68 | memmove(region->data[i], buf+((choff>>8)<<12), clen); 69 | region->len[i] = clen; 70 | } 71 | } 72 | 73 | lh_free(buf); 74 | return region; 75 | } 76 | 77 | // save a region to disk 78 | ssize_t anvil_save(mca *region, const char *path) { 79 | // generate chunk table first, looking at chunks availability and length 80 | uint8_t buf[8192]; 81 | int i; 82 | int choff=2; 83 | uint8_t *w = buf; 84 | 85 | // fill out the chunk offset and timstamp tables 86 | for(i=0; idata[i]) { 88 | int chlen = lh_align(region->len[i], 4096); 89 | lh_write_int_be(w, (choff<<8) + (chlen>>12)); 90 | //printf("Chunk %d (%d,%d), off=%d, len=%d\n", i, i%32, i/32, choff, (chlen>>12)); 91 | choff+=(chlen>>12); 92 | } 93 | else { 94 | lh_write_int_be(w, 0); 95 | } 96 | } 97 | 98 | for(i=0; its[i]); 100 | 101 | // write out the header tables 102 | int fd=lh_open_write(path); 103 | if (fd<0) return -1; 104 | lh_write(fd, buf, sizeof(buf)); 105 | 106 | // write out chunk data, padded with zeros to page size 107 | lh_clear_obj(buf); 108 | ssize_t sz = 8192; 109 | for(i=0; idata[i]) { 111 | int chlen = lh_align(region->len[i], 4096); 112 | lh_write(fd, region->data[i], region->len[i]); 113 | if (chlen > region->len[i]) 114 | lh_write(fd, buf, (chlen-region->len[i])); 115 | sz += chlen; 116 | } 117 | } 118 | close(fd); 119 | return sz; 120 | } 121 | 122 | static uint8_t nbtdata[2<<24]; // static buffer for NBT data 123 | static uint8_t cdata[2<<22]; // static buffer for compressed data 124 | 125 | // return decoded NBT data of a chunk from the region 126 | nbt_t * anvil_get_chunk(mca * region, int32_t X, int32_t Z) { 127 | // chunk index in the region - we can accept local and global coordinates 128 | int idx = (X&0x1f)+((Z&0x1f)<<5); 129 | 130 | // chunk is not available 131 | if (!region->data[idx]) return NULL; 132 | 133 | // parse chunk header 134 | uint8_t *p = region->data[idx]; 135 | uint32_t len = lh_read_int_be(p)-1; 136 | assert(region->len[idx]>=len+5); 137 | uint8_t ctype = lh_read_char(p); 138 | assert(ctype==1 || ctype==2); 139 | 140 | ssize_t dlen; 141 | if (ctype==1) 142 | dlen = lh_gzip_decode_to(p, len, nbtdata, sizeof(nbtdata)); 143 | else 144 | dlen = lh_zlib_decode_to(p, len, nbtdata, sizeof(nbtdata)); 145 | 146 | p = nbtdata; 147 | nbt_t * nbt = nbt_parse(&p); 148 | 149 | return nbt; 150 | } 151 | 152 | // add a chunk in NBT form to the region 153 | void anvil_insert_chunk(mca * region, int32_t X, int32_t Z, nbt_t *nbt) { 154 | // chunk index in the region - we can accept local and global coordinates 155 | int idx = (X&0x1f)+((Z&0x1f)<<5); 156 | 157 | // chunk is available - delete it 158 | lh_free(region->data[idx]); 159 | 160 | // serialize and compress chunk NBT 161 | uint8_t *w = nbtdata; 162 | nbt_write(&w, nbt); 163 | ssize_t clen = lh_zlib_encode_to(nbtdata, w-nbtdata, cdata, sizeof(cdata)); 164 | 165 | // store it in the region 166 | region->data[idx] = malloc(clen+5); 167 | region->len[idx] = clen+5; 168 | w = region->data[idx]; 169 | lh_write_int_be(w, (uint32_t)clen+1); 170 | lh_write_char(w, 2); 171 | memmove(w, cdata, clen); 172 | } 173 | 174 | nbt_t * anvil_tile_entities(gschunk * ch) { 175 | nbt_t *tent = NULL; 176 | if (ch->tent) 177 | tent = nbt_clone(ch->tent); 178 | else 179 | tent = nbt_new(NBT_LIST, "TileEntities", 0); 180 | return tent; 181 | } 182 | 183 | // generate chunk NBT from chunk_t data 184 | nbt_t * anvil_chunk_create(gschunk * ch, int X, int Z) { 185 | int y,i; 186 | 187 | // TODO: export entities and tile entities 188 | nbt_t * ent = nbt_new(NBT_LIST, "Entities", 0); 189 | nbt_t * tent = anvil_tile_entities(ch); 190 | 191 | // Calculate the height map - and fill out the cube mask 192 | int hmap[256]; 193 | lh_clear_obj(hmap); 194 | uint16_t mask = 0; 195 | 196 | for(i=65535; i>=0; i--) { 197 | if (ch->blocks[i].bid) { 198 | y=i>>8; 199 | mask |= (1<<(y>>4)); 200 | if (!hmap[i&0xff]) hmap[i&0xff]=y; 201 | } 202 | } 203 | 204 | // Block data 205 | nbt_t * sections = nbt_new(NBT_LIST, "Sections", 0); 206 | 207 | for(y=0; y<16; y++) { 208 | if (!(mask&(1<blocks[i+(y<<12)].bid; 214 | uint8_t meta = ch->blocks[i+(y<<12)].meta; 215 | if (i&1) 216 | data[i/2] |= (meta<<4); 217 | else 218 | data[i/2] = meta; 219 | } 220 | 221 | nbt_t * cube = nbt_new(NBT_COMPOUND, NULL, 5, 222 | nbt_new(NBT_BYTE_ARRAY, "Blocks", blocks, 4096), 223 | nbt_new(NBT_BYTE_ARRAY, "SkyLight", ch->skylight+(y<<11), 2048), 224 | nbt_new(NBT_BYTE, "Y", y), 225 | nbt_new(NBT_BYTE_ARRAY, "BlockLight", ch->light+(y<<11), 2048), 226 | nbt_new(NBT_BYTE_ARRAY, "Data", data, 2048) 227 | ); 228 | 229 | nbt_add(sections, cube); 230 | } 231 | 232 | // Chunk compound 233 | nbt_t *chunk = nbt_new(NBT_COMPOUND, "", 2, 234 | nbt_new(NBT_COMPOUND, "Level", 11, 235 | nbt_new(NBT_BYTE, "LightPopulated", 0), 236 | nbt_new(NBT_INT, "zPos", Z), 237 | nbt_new(NBT_INT_ARRAY, "HeightMap", hmap, 256), 238 | sections, // Block/light data 239 | nbt_new(NBT_LONG, "LastUpdate", 1240000000), //TODO: adjust timestamp 240 | nbt_new(NBT_BYTE_ARRAY, "Biomes", ch->biome, 256 ), 241 | nbt_new(NBT_LONG, "InhabitedTime", 0), 242 | nbt_new(NBT_INT, "xPos", X), 243 | nbt_new(NBT_BYTE, "TerrainPopulated", 1), 244 | tent, // TileEntities 245 | ent // Entities 246 | ), 247 | nbt_new(NBT_INT, "DataVersion", 512) 248 | ); 249 | 250 | return chunk; 251 | } 252 | -------------------------------------------------------------------------------- /anvil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "nbt.h" 6 | #include "mcp_gamestate.h" 7 | 8 | #define REGCHUNKS (32*32) 9 | 10 | typedef struct { 11 | uint8_t * data[REGCHUNKS]; // compressed chunk data 12 | ssize_t len[REGCHUNKS]; // size of each chunk's data 13 | uint32_t ts[REGCHUNKS]; // chunk timestamps 14 | } mca; 15 | 16 | mca * anvil_create(); 17 | void anvil_free(mca * region); 18 | void anvil_dump(mca * region); 19 | 20 | mca * anvil_load(const char *path); 21 | ssize_t anvil_save(mca *region, const char *path); 22 | 23 | nbt_t * anvil_get_chunk(mca * region, int32_t X, int32_t Z); 24 | void anvil_insert_chunk(mca * region, int32_t X, int32_t Z, nbt_t *nbt); 25 | 26 | nbt_t * anvil_chunk_create(gschunk * ch, int X, int Z); 27 | -------------------------------------------------------------------------------- /bigtest.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/bigtest.nbt -------------------------------------------------------------------------------- /docs/images/build_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/build_example.jpg -------------------------------------------------------------------------------- /docs/images/cactus_republic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/cactus_republic.jpg -------------------------------------------------------------------------------- /docs/images/checkerboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/checkerboard.jpg -------------------------------------------------------------------------------- /docs/images/mani_stacked.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/mani_stacked.jpg -------------------------------------------------------------------------------- /docs/images/mani_torches.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/mani_torches.jpg -------------------------------------------------------------------------------- /docs/images/mani_tower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/mani_tower.jpg -------------------------------------------------------------------------------- /docs/images/mani_trim.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/mani_trim.jpg -------------------------------------------------------------------------------- /docs/images/melon_castle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/melon_castle.jpg -------------------------------------------------------------------------------- /docs/images/pixelart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/pixelart.jpg -------------------------------------------------------------------------------- /docs/images/scan_after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/scan_after.jpg -------------------------------------------------------------------------------- /docs/images/scan_pos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/scan_pos.jpg -------------------------------------------------------------------------------- /docs/images/server_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/server_list.png -------------------------------------------------------------------------------- /docs/images/space_valley.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/space_valley.jpg -------------------------------------------------------------------------------- /docs/images/stairs1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/stairs1.jpg -------------------------------------------------------------------------------- /docs/images/stairs2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/stairs2.jpg -------------------------------------------------------------------------------- /docs/images/stairs3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/stairs3.jpg -------------------------------------------------------------------------------- /docs/images/wrath_outpost.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/docs/images/wrath_outpost.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MCBuild Documentation - Table of Contents 5 | 6 | 7 | 8 | 9 | 10 |

Welcome to the documentation for MCBuild, a custom Minecraft Client 11 | with auto-building function. Please select one of the topics below:

12 | 13 |

Table of Contents

14 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/install.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MCBuild Documentation - Installation 5 | 6 | 7 | 8 | 9 | 10 | 11 | Table of Contents | 12 | Previous: Introduction | 13 | Next: Interface 14 |
15 | 16 |

Installation

17 | 18 | 26 | 27 | 28 |

Supported Platforms

29 | 30 |

Currently 64-bit Linux and Windows are supported. Windows version 31 | uses Cygwin to simulate a POSIX-compatible environment and uses 32 | exactly the same source code and compiled the same way as the 33 | Linux version.

34 | 35 |

Building it involves only a few standard tools and libraries, which 36 | are easy set up on any Linux distribution or in Cygwin. I am 37 | also providing a prebuilt package for Windows, which is probably 38 | the most convenient choice for most users.

39 | 40 |

32-bit platforms work as well and there is a prebuilt package 41 | for 32-bit Windows available. However, 32-bit versions are not 42 | properly tested so there might be issues that went unnoticed. 43 | Other architectures (e.g. ARM on Raspberry Pie) should also work, 44 | but that wasn't tested - feel free to try.

45 | 46 |

What about OSX? Good question. OSX is UNIX-based and all the 47 | necessary tools and libraries are available - it should just work 48 | if you install GCC and other tools, essentialy following the Linux 49 | installation instructions. Unfortunately I have no experience with 50 | OSX and no possibility to test it. If someone's willing to 51 | volunteer to test the installation procedure and create an 52 | installation guide, I would gladly add it to the documentation.

53 | 54 |

MCBuild is written completely in C, so it's very fast and lightweight. 55 | You should be able to run it even on low-end hardware.

56 | 57 |

Follow one of the three sections below that suits you best.

58 | 59 |
60 |

Installation Procedure on Windows (precompiled package)

61 | 62 |

Download the package from the link provided on the forums and 63 | unpack it to your C:\ drive. At the end, you should have a C:\cygwin64 64 | directory (C:\cygwin for the 32-bit version) with a bunch of 65 | other files and directories in it.

66 | 67 |

In the C:\cygwin64 directory, launch the cygwin.bat file. 68 | On the first launch you will see a bunch of files being copied - 69 | this is your home directory being set up. After that, you will 70 | get a shell prompt.

71 | 72 |

Now you can proceed with the section 73 | Configuring Your Minecraft Client.

74 | 75 | 76 | 77 | 78 |

Installation Procedure on Windows (manual)

79 | 80 |

Download and launch the 81 | Cygwin 64-bit installer.. During installation wizard, make 82 | sure to select a mirror location close to you. Leave other settings 83 | as they are.

84 | 85 |

When you reach the package manager screen, select following 86 | additional packages to be installed:

87 | 88 |
    89 |
  • Libs/libcurl-devel
  • 90 |
  • Libs/zlib-devel
  • 91 |
  • Libs/libpng-devel
  • 92 |
  • Libs/libisl10
  • 93 |
  • Libs/libcloog-isl4
  • 94 |
  • Libs/libjson-c-devel
  • 95 |
  • Net/openssl-devel
  • 96 |
  • Devel/git
  • 97 |
  • Devel/gcc-core
  • 98 |
  • Devel/make
  • 99 |
  • Devel/makedepend
  • 100 |
  • Archive/unzip
  • 101 |
  • Archive/zip
  • 102 |
103 | 104 |

The simplest way to locate them, is to type the name of the 105 | package (e.g. "libcurl-devel" into the search bar, then select 106 | the package under correct folder. Note that there are packages 107 | with similar names located in the same or different folders - 108 | make sure to select the right one!

109 | 110 |

To select a package for install, click on the "Skip" next to 111 | its name - it should then change to the version number Cygwin 112 | is going to install.

113 | 114 |

After you selected all the packages, confirm everything and 115 | let it install. In case you missed something, you can always 116 | launch the installer again - it will recognize the current 117 | installation and what packets were already installed.

118 | 119 |

Now you can launch the shell in C:\cygwin64\cygwin.bat or 120 | from the shortcut installed on your desktop.

121 | 122 |

Now you should have all necessary tools and libraries, but 123 | you still need to install MCBuild. You can proceed with the 124 | manual MCBuild installation, which 125 | is essentially identical on Windows and Linux.

126 | 127 | 128 | 129 |

Installation Procedure on Linux (manual)

130 | 131 |

You need to install following tools and libraries. 132 | The exact names of the packages and how do you install 133 | them may differ from distro to distro, but all of them 134 | should be available on any decent Linux distro. Refer to your 135 | package manager.

136 | 137 |

For the libraries, make sure to install a package containing 138 | the files necessary for the development, i.e. header files 139 | etc. Typically these packages are are identified by -devel 140 | suffix. I.e. on Ubuntu you would typically have libpng package 141 | already installed, but you need libpng-devel in order to compile 142 | MCBuild.

143 | 144 |
    145 |
  • Tools
  • 146 |
      147 |
    • gcc
    • 148 |
    • gmake
    • 149 |
    • bash
    • 150 |
    • git
    • 151 |
    • gawk
    • 152 |
    • makedepend
    • 153 |
    • zip
    • 154 |
    • unzip
    • 155 |
    156 |
  • Libraries
  • 157 |
      158 |
    • libcurl
    • 159 |
    • openssl
    • 160 |
    • zlib
    • 161 |
    • libpng
    • 162 |
    • json-c
    • 163 |
    164 |
165 | 166 |
167 |

Compiling MCBuild

168 | 169 |

At this point, you should have all the prerequisities installed 170 | and the procedure will be identical for Windows or Linux installation 171 | from scratch. We assume that MCBuild and libhelper (a library used by 172 | it) will be installed into your home directory.

173 | 174 |

Clone and compile libhelper and MCBuild sources:

175 | 176 |
177 | cd
178 | git clone https://github.com/broese/libhelper.git
179 | cd libhelper
180 | make
181 | 
182 | cd
183 | git clone https://github.com/broese/mcbuild.git
184 | cd mcbuild
185 | make
186 | make install
187 | export PATH="$HOME/bin:$PATH"
188 |       
189 | 190 |

If everything went fine, you should now have mcproxy application in that 191 | directory (mcproxy.exe on Cygwin) - the actual proxy/client and a couple 192 | of other auxiliary applications like mcpdump which we will describe later. 193 | Now MCBuild should be ready to use. Proceed with the next section 194 | to configure your Minecraft client.

195 | 196 |
197 |

Configuring your Minecraft Client for Use with MCBuild

198 | 199 |

For your convenience, I've provided a bunch of scripts to assist you 200 | with the setup. You can execute them at the shell prompt (Linux or 201 | Cygwin). Before continuing, you should make sure that MCBuild is 202 | updated to the newest version:

203 | 204 |
205 | mcb_update
206 | 
207 | 208 |

If you just happen to have a fresh installation of Minecraft, 209 | and no profile has been set up yet, please login and launch the 210 | client at least once before using MCBuild.

211 | 212 |

Add a new entry to your multiplayer server list, through which you 213 | will be accessing MCBuild. Use address 127.0.0.1

214 | 215 | 216 | 217 |

Now you are ready to launch MCBuild.

218 | 219 |
220 |

Running MCBuild

221 | 222 |

You can launch MCBuild from the shell prompt, using

223 | 224 |
225 | mcb_start
226 | 
227 | 228 |

When launched without parameters, MCBuild will connect to 2b2t.org. 229 | To connect to any other server, write the address as a parameter:

230 | 231 |
232 | mcb_start mc.example.com
233 | 
234 | 235 | - If your server uses a different port number than the default 25565, 236 | you can specify that as well: 237 | 238 |
239 | mcb_start mc.example.com:12345
240 | 
241 | 242 |

Now you should be able to connect to your server through MCBuild, by 243 | accessing the server list entry we defined before.

244 | 245 |

If everything goes well and you're logged in, you can do a quick test 246 | to verify MCBuild really works by typing in chat:

247 | 248 |
249 | #test
250 | 
251 | 252 |

You should see a response from MCBuild. Voila! You're ready to go!

253 | 254 |

For the further instructions, refer to the documentation located in 255 | the docs folder. If you're using Windows version, it should be located at 256 | c:\cygwin64\home\YOURNAME\mcbuild\docs\index.html and on Linux 257 | ~/mcbuild/docs/index.html

258 | 259 |
260 |
Top of page | 261 | Table of Contents | 262 | Previous: Introduction | 263 | Next: Interface 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /docs/interface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MCBuild Documentation - Interface 5 | 6 | 7 | 8 | 9 | 10 | 11 | Table of Contents | 12 | Previous: Installation | 13 | Next: Building Guide 14 |
15 | 16 |

Interface

17 | 18 |

This section gives you the overview of the chat interface 19 | and the list of available commands.

20 | 21 |

MCBuild is controlled mostly via commands issued in the chat, 22 | and also issues notifications back to you via chat. The chat 23 | notifications will be preceeded with <MCP>, 24 | and be colored, so you can differentiate them from the 25 | general chat. Some notifications will appear in the 26 | "Pop-up" chat area above your hotbar. 27 | 28 |

Certain actions however are done via interaction with the world - 29 | so for example, the pivot point to start building can be 30 | specified by placing a block there, and the in-world preview 31 | will spawn visible fantom blocks in the world.

32 | 33 |

Some other commands, or asynchronous processes will print 34 | information to the terminal - otherwise it would be too 35 | verbose for the chat. Usually it serves debug purposes, 36 | and can be ignored. The one exception maybe is the 37 | '#build material' command to show the summary of required 38 | materials for a building.

39 | 40 |

All MCBuild-related commands start with a '#' as the first 41 | symbol. MCBuild will intercept these messages and will not 42 | forward them into general chat. Even if you specify an incorrect 43 | or unrecognized command, it will not be forwarded further 44 | to the chat, but instead ignored.

45 | 46 |

Some of the custom clients posess a convenient feature to 47 | bind certain chat messages to hotkeys. This feature will 48 | be very useful with MCBuild, as you can summon certain 49 | frequently used commands like '#build pause' or '#build preview' 50 | with just a single key press. Examples of custom clients 51 | with this feature are Metro and Weepcraft

52 | 53 | 54 |

List of commands

55 |

Below is the list of all commands currently supported by 56 | MCBuild. Many of them are simple toggles, so I will describe 57 | them directly in the table. Some other commands are more 58 | complex and have parameters, especially those related to 59 | building. Those will have links to the separate sections 60 | describing them in more detail.

61 | 62 |

Commands have a full name and a shorter name provided for 63 | convenience. So, for the building-related commands you 64 | can use '#build' or '#bu' interchangingly. Same applies 65 | to the most parameters, but these will be described in 66 | a separate section.

67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 99 | 100 | 101 | 102 | 103 | 104 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 190 | 191 | 192 | 193 | 194 | 195 | 198 | 199 |
NameShortTypeDescription
alignparamsAlign the player exactly to a world direction and block center. Useful for autowalking nether tunnels.
81 | align - align to exact axial direction in the player's look direction
82 | align <yaw> - align to exact yaw angle (float value). 0: South, 90: West, 180: North, 270:East 83 |
antiafkafktoggleAnti-AFKing - repeatedly places and removes a torch beneath your feet. You must have torches in your inventory
autokillaktoggleKill-aura. Only attacks hostile monsters. Pigmen are not attacked, but can be allowed as well with the -p option.
96 | ak - toggle autokill
97 | ak -p - enable autokill and allow attacking pigmen too. 98 |
autologoutaltoggleAutomatically log out when health falls below defined level.
105 | autologoutAuto-logout when health drops below 15
106 | autologout <hp>Auto-logout when health drops below specified HP
107 |
autoshearashtoggleAutomatically shear sheep in your vicinity. Only activates when you hold shears as your active item
buildbuparamsBuild functions. Refer to the building guide or the summary table of building commands below
freecamfctoggleFreecam - switches into spectator mode allowing you to freely move the camera around available chunks.
grindparamsAutokill monsters until a certain experience level is reached. Use in a XP grinder.
132 | grind - Grind until level 30
133 | grind <n> - Grind until level n
134 | grind stop - Stop grinding
135 |
holeradarhrtoggleWarn about holes on your way. Useful when walking or digging nether tunnels
hudparamsEnable a HUD for representing various graphical information on your screen using a map item. 148 | You need at least one free slot to receive a bogus map item which you can hold to display one 149 | of the screens. Best to keep it in the left-hand slot. 150 |
xraytoggleX-Ray - make common blocks transparent. Currently not customizable.
testdebugTest MCBuild response - use this command to check if MCBuild was set up properly
entitiesdebugDump the table of tracked entities
coordsdebugPrint your current coords (block coords of your feet)
invdebugPrint your inventory to the terminal
changehelddebugChange the active hotbar slot.
187 | changeheld <sid> - switch to hotbar slot sid (0-9)
188 | changeheld <sid> 1 - switch to hotbar slot sid and notify client
189 |
swapslotsdebugSwap items in two slots in the inventory
196 | swapslots <sid1> <sid2> - swap items in the inventory slots sid1 and sid2 (0-45) 197 |
200 | 201 |
202 |

List of building commands

203 |

Below is the summary table of all building-related 204 | commands. A more detailed overview of the building 205 | process and parameters if given in the 206 | building guide. This table 207 | only serves as a quick cheat-sheet

208 | 209 |

All building commands have form '#build command_name [parameters]' 210 | or can be shortened to '#bu command_name [parameters]'.

211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 367 | 368 | 369 | 370 | 371 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 |
NameParametersDescription
Parametric Builds
floorsize,materialSolid rectangular floor WxL
wallsize,materialSolid rectangular wall WxH
diskdiameter,materialSolid disk with diameter D
balldiameter,materialSolid ball with diameter D
ringdiameter,materialRing with diameter D
spherediameter,materialHollow sphere with diameter D
squaresize,materialHollow rectangular floor WxL
scaffolding,scafsize,[material],[-ladder]Scaffolding width W, H floors. -ladder for an alternative design using ladders
stairssize,material,[-none|minimal|full],[-exact]Stairs
Buildplan Manipulation
extend,extoffset|direction,countExtend the buildplan multiple times with given offset or direction
hollow[-flat],[-opaque]Hollow out a 3D structure, use -flat for 2D structures, consider all blocks opaque with -opaque
replacematerial1,material2Replace specific material in the buildplan with another. Use ID=0 (Air) to delete blocks
trimtrimspec ...Trim the buildplan leaving only blocks mathcing the trimspec (e.g. y>=3)
flip[axis]Flip the buildplan along specified axis (x by default)
tilt[axis]Tilt/rotate the buildplan by 90 degrees around specified axis (x by default)
normalize,normShift the entire buildplan so that pivot block is in the leftmost, nearest, bottom point
shrinkShrink the buildplan by factor 2. Only works well for solid builds
scale[factor]Scale up the buildplan by given integer factor. Only works well for solid builds
Import/Export
savefilenameExport buildplan to .bplan format
tsave[filename]Save current buildtask to a backup .bplan file
ssavefilenameExport buildplan to .schematic format
csvsavefilenameExport buildplan to .csv format
loadfilenameImport buildplan from .bplan file
tload[filename]Load buildtask from a backup .bplan file
sloadfilenameImport buildplan from .schematic file
csvloadfilenameImport buildplan from .csv file
pngload,pngfilename,[blockset]Import buildplan from .png file as a pixelart wall 366 | and using a specific blockset (wool by default)
recstart|stop|resumeBuild recorder - will record placed blocks, creating 372 | a buildplan. First placed block is the pivot.
scanpivot,sizeScan a cuboid of given size and pivot from the world to produce a buildplan.
Build Control
placeonce|many|again|cancel,[pivot]Initiate a buildplan placement - once or many times, 385 | or using the previous pivot position, or cancel the placement. 386 | Pivot can be specified explicitly or by placing a block.
cancelCancel building process
pausePause/unpause building process
preview-true|missing|removeGenerate or remove an in-world preview of the buildplan. 402 | -missing mode shows blocks still missing or placed incorrectly
limit,li[ycoord|-]Set a vertical limit for building and preview - useful for building layer by layer
opt,set[option_name=value]Set a building option (standard delays, etc.). Without arguments prints current values.
Debug
mat[-p]Print a summary of required material to the terminal
dumpplanDump buildplan to terminal
dumptaskDump buildtask to terminal
dumpqueueDump blocks immediately scheduled for placement to terminal
439 | 440 |
441 | Top of page | 442 | Table of Contents | 443 | Previous: Installation | 444 | Next: Building Guide 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /docs/intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MCBuild Documentation - Introduction 5 | 6 | 7 | 8 | 9 | 10 | Table of Contents | 11 | - | 12 | Next: Installation 13 |
14 | 15 |

Introduction

16 |

MCBuild is a custom client for Minecraft with a lot of additional 17 | functions but it's primary emphasis is on auto-building. It is capable 18 | of building in creative or survival mode, and will work on servers 19 | with Anti-cheat, however due to its nature, it will work in multiplayer 20 | only. If you want to use it in a single-player game, you nevertheless 21 | can run your own server locally and load it with your SP world, or 22 | access a single-player game shared by another user on the network.

23 | 24 |

Unlike many custom/hacked clients available around, mcbuild uses 25 | a completely different approach. It is not quite correct to call 26 | it a client at all - instead it's a proxy-like application that 27 | sits between your client and the server you play on and manipulates 28 | Minecraft protocol only. It passes most Minecraft traffic transparently, 29 | while inserting, dropping or modifying some packets as needed.

30 | 31 |

This has both advantages and drawbacks. On one hand you don't 32 | need to give up on your favorite client or apply any mods to it 33 | - you can continue using it as is. If you have a hacked client 34 | or mods you use, the functions from mcbuild will add to them, 35 | not replace them. On the other hand, mcbuild lacks the possibility 36 | to add any GUI elements on your client and the only way to control 37 | it or receive information from it is via minecraft protocol. Most 38 | commands and notifications happen through the chat, and a few by 39 | means of spawning phantom blocks or entities. Consider it like 40 | this - mcbuild is a CLI, not a GUI application and it will take 41 | some ambition to learn to use it, but I hope you will appreciate 42 | its potential.

43 | 44 | 45 |

History

46 |

MCBuild started back in 2012 as "minemap" - a mere mapping tool for 47 | Minecraft. Initially tested for the Anvil map data generated in the 48 | single play, I wanted to extend it to the multiplayer games as well 49 | and started reading on the Minecraft protocol. At that time, 50 | Minecraft 1.2 was the current version and I figured out, the map 51 | data could be simply extracted from the network traffic - I just 52 | needed to record my gaming session with Wireshark and extract necessary 53 | messages from the PCAP.

54 | 55 |

But starting with the version 1.3, Minecraft intrroduced protocol 56 | encryption and the capturing method could be no longer used. So, I 57 | came with the idea of "mcproxy" - a transparent proxy for the Minecraft 58 | protocol, that would hook into encrypted connection between the client 59 | and the server by using the man-in-the-middle (MITM) principle. The 60 | standard Minecraft client would connect to the proxy, and the proxy 61 | would connect to the remote server, passing all traffic transparently. 62 | Since the proxy process establishes the encryption session on both sides, 63 | it will have access to the plaintext traffic. I quickly realized 64 | that this setup has more potential than just passively mapping stuff.

65 | 66 |

At that time I joined the infamous 2b2t.net server (before it became 67 | .org). I'm still an active player there and most of the functions 68 | developed for MCBuild related to my experience on that server. Even 69 | the default server to connect to is 2b2t. The most famous feature 70 | of that server is that the map was never reset, and survived through 71 | all generations of Minecraft starting from the Alpha times at the spawn, 72 | this, and also a huge number of players that went through the server, 73 | combined with the absolute anarchy and massive use of hacked clients 74 | has left the spawn area resembling a nuclear explosion site. 75 | Even many miles across, the players managed to wreck the world beyond 76 | recognition.

77 | 78 |

The first problem every newbie player like myself was facing is escaping 79 | the spawn and finding any wood, essential to basic survival. The first 80 | tree I found was over 1.5km away from my spawn point - everything closer 81 | was ripped out or burned by the endless herds of players. But the next 82 | step was a bit trickier - as I wanted to set up my first enchanting table, 83 | I could not find any sugarcane many miles around to make my books. This 84 | is where I started extending the functionality with a simple function to 85 | search for certain blocks. I could find whole 3 blocks of sugarcane in the 86 | 4 km radius from spawn - by some terrain generator glitch they landed deep 87 | underwater and so survived the greedy players.

88 | 89 |

Later I started extending the functionality with various hacking 90 | functions like self-made Killaura, player notification, entity finder, 91 | etc. Then, in 2014 came the idea to implement the auto-building. I 92 | completely redesigned the mcproxy from scratch and started implementing 93 | first auto-building features. The development was a bit delayed by the 94 | switch to a new Minecraft protocol which came with 1.6.6 and a complete 95 | redesign of the framework to cope with the increased complexity of the 96 | code.

97 | 98 |

The development progressed slowly, but culminated in April/May 2015, as I 99 | participated in the April Fools and then in the Third Spawn Incursion, 100 | the yearly events semi-spontaneously happening on 2b2t. In the April Fools 101 | event I already had a somewhat reliably working implementation which I 102 | used to build a crude castle in the Cactus Republic base.

103 | 104 | 105 | 106 |

After the April Fools event, I went on more implementing and testing 107 | in the harsh conditions of 2b2t and started building a secret "Rocket 108 | Valley", with its first exhibits - a full size Saturn V and Space 109 | Shuttle replicas (models courtesy 110 | Eocen and 111 | DNV970 112 | at PlanetMinecraft)

113 | 114 | 115 | 116 |

Finally, in May 2015 I participated in the Third Spawn Incursion event. 117 | At that point, MCBuild gathered a lot of new features, fixes and 118 | special building modes making building more efficient and safe in survival, 119 | anti-cheat and exceptionally laggy environment of 2b2t. The event 120 | culminated with the erection of the Wrath Outpost (aka SIB - Spawn 121 | Incursion Base) an enormous obsidian structure for which I built the 122 | most of the great arches,the curved entrace roof and the dome, as 123 | well as some minor elements.

124 | 125 | 126 | 127 |

Another prominent building sitting right next to Wrath Outpost was 128 | my Melon Castle - a castle built completely out of ~8000 melon blocks. 129 | Although it was destroyed at least 4 times during the Incursion, 130 | I was able to quickly rebuild it. The last version was still 131 | standing 2 months later - heavily nibbled upon by the hungry spawn escapees.

132 | 133 | 134 | 135 |

Later in the Summer 2015, I used MCBuild to build some of the 136 | structures in the King's Landing base. All these events provided 137 | a huge amount of experience that helped me eliminate more bugs, 138 | extend functionality and make improvements into building process. 139 | Finally the development came to the stage where I could release it 140 | as a more-or-less feature complete, stable and documented version 141 | to the general public. Please enjoy using it, bug reports and 142 | suggestions for improvements are welcome!

143 | 144 | 145 |

License

146 |

MCBuild and its direct dependency libhelper are licensed under GPL2.

147 | 148 |
149 |
Top of page | 150 | Table of Contents | 151 | - | 152 | Next: Installation 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | max-width: 1200px; 3 | margin: 0 auto; 4 | } 5 | 6 | table, th, td { 7 | border: 1px solid gray; 8 | border-collapse: collapse; 9 | } 10 | 11 | th { 12 | background-color:#e0e0ff; 13 | } 14 | 15 | td.separator { 16 | background-color:#e0e0e0; 17 | font-weight: bold; 18 | } 19 | 20 | th, td { 21 | padding: 5px; 22 | text-align: left; 23 | } 24 | 25 | .striped tr:nth-child(odd) { 26 | background-color:#ffffe0; 27 | } 28 | 29 | tr.toned { 30 | background-color:#ffffe0; 31 | } 32 | 33 | tt { 34 | background-color:#ffffe0; 35 | font-weight: bold; 36 | padding: 5px; 37 | } 38 | 39 | .shell { 40 | background-color: #333; 41 | color: #3F3; 42 | border-radius: 4px; 43 | padding: 4px; 44 | margin-bottom: 10px; 45 | overflow: auto; 46 | } 47 | 48 | h1 { 49 | background-color: #ffd; 50 | color: #002; 51 | border-radius: 12px; 52 | padding: 12px; 53 | margin-bottom: 10px; 54 | overflow: auto; 55 | } 56 | 57 | h2 { 58 | background-color: #fdf; 59 | color: #020; 60 | border-radius: 8px; 61 | padding: 8px; 62 | margin-bottom: 10px; 63 | overflow: auto; 64 | } 65 | 66 | h3 { 67 | background-color: #dff; 68 | color: #200; 69 | border-radius: 4px; 70 | padding: 4px; 71 | margin-bottom: 10px; 72 | overflow: auto; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /dumpreg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "anvil.h" 5 | #include "nbt.h" 6 | 7 | int main(int ac, char **av) { 8 | if (!av[1]) { 9 | printf("Usage: %s [X,Z]\n", av[0]); 10 | exit(1); 11 | } 12 | 13 | int X=0, Z=0; 14 | if (av[2]) { 15 | if (sscanf(av[2], "%u,%u", &X, &Z)!=2 || X>31 || Z>31) { 16 | printf("Usage: %s [X,Z]\n", av[0]); 17 | exit(1); 18 | } 19 | } 20 | 21 | mca * reg = anvil_load(av[1]); 22 | if (!reg) { 23 | printf("Failed to load Anvil file %s\n", av[1]); 24 | exit(1); 25 | } 26 | 27 | nbt_t * ch = anvil_get_chunk(reg, X, Z); 28 | if (!ch) { 29 | printf("Chunk not found at %s %d,%d\n", av[1], X, Z); 30 | exit(1); 31 | } 32 | nbt_dump(ch); 33 | 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /entity.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2016 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | /* 12 | entity : entities and entity metadata handling 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define LH_DECLARE_SHORT_NAMES 1 21 | #include 22 | #include 23 | #include 24 | 25 | #include "entity.h" 26 | #include "mcp_packet.h" 27 | 28 | // Entities 29 | 30 | const char * METANAME[][32] = { 31 | [Entity] = { 32 | [0] = "Flags", 33 | [1] = "Air", 34 | [2] = "Custom name", 35 | [3] = "Name visible", 36 | [4] = "Is silent", 37 | [5] = "No gravity", 38 | }, 39 | [Potion] = { 40 | [6] = "Slot", 41 | }, 42 | [FallingBlock] = { 43 | [6] = "Position", 44 | }, 45 | [AreaEffectCloud] = { 46 | [6] = "Radius", 47 | [7] = "Color", 48 | [8] = "Single point", 49 | [9] = "Particle ID", 50 | [10] = "Particle Parameter 1", 51 | [11] = "Particle Parameter 2", 52 | }, 53 | [FishingFloat] = { 54 | [6] = "Hooked entity", 55 | }, 56 | [Arrow] = { 57 | [6] = "Is critical", 58 | }, 59 | [TippedArrow] = { 60 | [7] = "Color", 61 | }, 62 | [Boat] = { 63 | [6] = "Time since hit", 64 | [7] = "Forward direction", 65 | [8] = "Damage taken", 66 | [9] = "Type", 67 | [10] = "Right paddle turning", 68 | [11] = "Left paddle turning", 69 | }, 70 | [EnderCrystal] = { 71 | [6] = "Beam target", 72 | [7] = "Show bottom", 73 | }, 74 | [Fireball] = { 75 | }, 76 | [WitherSkull] = { 77 | [6] = "Invulnerable", 78 | }, 79 | [Fireworks] = { 80 | [6] = "Firework info", 81 | [7] = "Boosted entity ID", 82 | }, 83 | [Hanging] = { 84 | }, 85 | [ItemFrame] = { 86 | [6] = "Item", 87 | [7] = "Rotation", 88 | }, 89 | [Item] = { 90 | [6] = "Item", 91 | }, 92 | [Living] = { 93 | [6] = "Active hand", 94 | [7] = "Health", 95 | [8] = "Potion effect color", 96 | [9] = "Potion effect ambient", 97 | [10] = "Number of arrows", 98 | }, 99 | [Player] = { 100 | [11] = "Additional hearts", 101 | [12] = "Score", 102 | [13] = "Skin flags", 103 | [14] = "Main hand", 104 | [15] = "Left shoulder", 105 | [16] = "Right shoulder", 106 | }, 107 | [ArmorStand] = { 108 | [11] = "Armor stand flags", 109 | [12] = "Head position", 110 | [13] = "Body position", 111 | [14] = "L arm position", 112 | [15] = "R arm position", 113 | [16] = "L leg position", 114 | [17] = "R leg position", 115 | }, 116 | [Insentinent] = { 117 | [11] = "Insentinent flags", 118 | }, 119 | [Ambient] = { 120 | }, 121 | [Bat] = { 122 | [12] = "Is hanging", 123 | }, 124 | [Creature] = { 125 | }, 126 | [Ageable] = { 127 | [12] = "Is baby", 128 | }, 129 | [Animal] = { 130 | }, 131 | [Horse] = { 132 | [13] = "Horse flags", 133 | [14] = "Horse type", 134 | [15] = "Horse color", 135 | [16] = "Owner UUID", 136 | [17] = "Horse armor", 137 | }, 138 | [Pig] = { 139 | [13] = "Has saddle", 140 | [14] = "Carrot boost time", 141 | }, 142 | [Rabbit] = { 143 | [13] = "Rabbit type", 144 | }, 145 | [PolarBear] = { 146 | [13] = "Standing", 147 | }, 148 | [Sheep] = { 149 | [13] = "Sheep color", 150 | }, 151 | [TameableAnimal] = { 152 | [13] = "Tameable flags", 153 | [14] = "Owner UUID", 154 | }, 155 | [Ocelot] = { 156 | [15] = "Ocelot type", 157 | }, 158 | [Wolf] = { 159 | [15] = "Damage", 160 | [16] = "Begging", 161 | [17] = "Collar color", 162 | }, 163 | [Parrot] = { 164 | [15] = "Variant", 165 | }, 166 | [Villager] = { 167 | [13] = "Profession", 168 | }, 169 | [Golem] = { 170 | }, 171 | [IronGolem] = { 172 | [12] = "created by player", 173 | }, 174 | [Snowman] = { 175 | [12] = "Flags", 176 | }, 177 | [Shulker] = { 178 | [12] = "Direction", 179 | [13] = "Attachment position", 180 | [14] = "Shield height", 181 | }, 182 | [Monster] = { 183 | }, 184 | [Blaze] = { 185 | [12] = "On fire", 186 | }, 187 | [Creeper] = { 188 | [12] = "Creeper state", 189 | [13] = "Charged", 190 | [14] = "Ignited", 191 | }, 192 | [Guardian] = { 193 | [12] = "Flags", 194 | [13] = "Target EID", 195 | }, 196 | [Skeleton] = { 197 | [12] = "Skeleton type", 198 | [13] = "Targeting", 199 | }, 200 | [Spider] = { 201 | [12] = "Climbing", 202 | }, 203 | [Witch] = { 204 | [12] = "Aggressive", 205 | }, 206 | [Wither] = { 207 | [12] = "Target 1", 208 | [13] = "Target 2", 209 | [14] = "Target 3", 210 | [15] = "Invulnerable time", 211 | }, 212 | [Zombie] = { 213 | [12] = "Baby zombie", 214 | [13] = "Villager zombie", 215 | [14] = "Converting zombie", 216 | [15] = "Hands up", 217 | }, 218 | [Enderman] = { 219 | [12] = "Carried block", 220 | [13] = "Screaming", 221 | }, 222 | [Enderdragon] = { 223 | [12] = "Phase", 224 | }, 225 | [Flying] = { 226 | }, 227 | [Ghast] = { 228 | [12] = "Attacking", 229 | }, 230 | [Slime] = { 231 | [12] = "Size", 232 | }, 233 | [Minecart] = { 234 | [6] = "Shaking power", 235 | [7] = "Shaking direction", 236 | [8] = "Shaking multiplier", 237 | [9] = "Block id/data", 238 | [10] = "Block y", 239 | [11] = "Show block", 240 | }, 241 | [MinecartCommandBlock] = { 242 | [12] = "Command", 243 | [13] = "Last Output", 244 | }, 245 | [MinecartFurnace] = { 246 | [12] = "Powered", 247 | }, 248 | [ActivatedTNT] = { 249 | [6] = "Fuse time", 250 | }, 251 | }; 252 | 253 | const char * METANAME_1_9_4[][32] = { 254 | [Entity] = { 255 | [0] = "Flags", 256 | [1] = "Air", 257 | [2] = "Custom name", 258 | [3] = "Name visible", 259 | [4] = "Is silent", 260 | }, 261 | [Potion] = { 262 | [5] = "Slot", 263 | }, 264 | [FallingBlock] = { 265 | [5] = "Position", 266 | }, 267 | [AreaEffectCloud] = { 268 | [5] = "Radius", 269 | [6] = "Color", 270 | [7] = "Single point", 271 | [8] = "Particle ID", 272 | }, 273 | [FishingFloat] = { 274 | [5] = "Hooked entity", 275 | }, 276 | [Arrow] = { 277 | [5] = "Is critical", 278 | }, 279 | [TippedArrow] = { 280 | [6] = "Color", 281 | }, 282 | [Boat] = { 283 | [5] = "Time since hit", 284 | [6] = "Forward direction", 285 | [7] = "Damage taken", 286 | [8] = "Type", 287 | [9] = "Paddle A", 288 | [10] = "Paddle B", 289 | }, 290 | [EnderCrystal] = { 291 | [5] = "Beam target", 292 | [6] = "Show bottom", 293 | }, 294 | [Fireball] = { 295 | }, 296 | [WitherSkull] = { 297 | [5] = "Invulnerable", 298 | }, 299 | [Fireworks] = { 300 | [5] = "Firework info", 301 | }, 302 | [Hanging] = { 303 | }, 304 | [ItemFrame] = { 305 | [5] = "Item", 306 | [6] = "Rotation", 307 | }, 308 | [Item] = { 309 | [5] = "Item", 310 | }, 311 | [Living] = { 312 | [5] = "Active hand", 313 | [6] = "Health", 314 | [7] = "Potion effect color", 315 | [8] = "Potion effect ambient", 316 | [9] = "Number of arrows", 317 | }, 318 | [Player] = { 319 | [10] = "Additional hearts", 320 | [11] = "Score", 321 | [12] = "Skin flags", 322 | [13] = "Main hand", 323 | }, 324 | [ArmorStand] = { 325 | [10] = "Armor stand flags", 326 | [11] = "Head position", 327 | [12] = "Body position", 328 | [13] = "L arm position", 329 | [14] = "R arm position", 330 | [15] = "L leg position", 331 | [16] = "R leg position", 332 | }, 333 | [Insentinent] = { 334 | [10] = "Insentinent flags", 335 | }, 336 | [Ambient] = { 337 | }, 338 | [Bat] = { 339 | [11] = "Is hanging", 340 | }, 341 | [Creature] = { 342 | }, 343 | [Ageable] = { 344 | [11] = "Is baby", 345 | }, 346 | [Animal] = { 347 | }, 348 | [Horse] = { 349 | [12] = "Horse flags", 350 | [13] = "Horse type", 351 | [14] = "Horse color", 352 | [15] = "Owner UUID", 353 | [16] = "Horse armor", 354 | }, 355 | [Pig] = { 356 | [12] = "Has saddle", 357 | }, 358 | [Rabbit] = { 359 | [12] = "Rabbit type", 360 | }, 361 | [Sheep] = { 362 | [12] = "Sheep color", 363 | }, 364 | [TameableAnimal] = { 365 | [12] = "Tameable flags", 366 | [13] = "Owner UUID", 367 | }, 368 | [Ocelot] = { 369 | [14] = "Ocelot type", 370 | }, 371 | [Wolf] = { 372 | [14] = "Damage", 373 | [15] = "Begging", 374 | [16] = "Collar color", 375 | }, 376 | [Villager] = { 377 | [12] = "Profession", 378 | }, 379 | [Golem] = { 380 | }, 381 | [IronGolem] = { 382 | [11] = "created by player", 383 | }, 384 | [Snowman] = { 385 | [10] = "Flags", 386 | }, 387 | [Shulker] = { 388 | [11] = "Direction", 389 | [12] = "Attachment position", 390 | [13] = "Shield height", 391 | }, 392 | [Monster] = { 393 | }, 394 | [Blaze] = { 395 | [11] = "On fire", 396 | }, 397 | [Creeper] = { 398 | [11] = "Creeper state", 399 | [12] = "Charged", 400 | [13] = "Ignited", 401 | }, 402 | [Guardian] = { 403 | [11] = "Flags", 404 | [12] = "Target EID", 405 | }, 406 | [Skeleton] = { 407 | [11] = "Skeleton type", 408 | [12] = "Targeting", 409 | }, 410 | [Spider] = { 411 | [11] = "Climbing", 412 | }, 413 | [Witch] = { 414 | [11] = "Aggressive", 415 | }, 416 | [Wither] = { 417 | [11] = "Target 1", 418 | [12] = "Target 2", 419 | [13] = "Target 3", 420 | [14] = "Invulnerable time", 421 | }, 422 | [Zombie] = { 423 | [11] = "Baby zombie", 424 | [12] = "Villager zombie", 425 | [13] = "Converting zombie", 426 | [14] = "Hands up", 427 | }, 428 | [Enderman] = { 429 | [11] = "Carried block", 430 | [12] = "Screaming", 431 | }, 432 | [Enderdragon] = { 433 | [11] = "Phase", 434 | }, 435 | [Flying] = { 436 | }, 437 | [Ghast] = { 438 | [11] = "Attacking", 439 | }, 440 | [Slime] = { 441 | [11] = "Size", 442 | }, 443 | [Minecart] = { 444 | [5] = "Shaking power", 445 | [6] = "Shaking direction", 446 | [7] = "Shaking multiplier", 447 | [8] = "Block id/data", 448 | [9] = "Block y", 449 | [10] = "Show block", 450 | }, 451 | [MinecartFurnace] = { 452 | [11] = "Powered", 453 | }, 454 | }; 455 | 456 | const EntityType ENTITY_HIERARCHY[] = { 457 | //// Superclasses 458 | [Entity] = IllegalEntityType, 459 | [Potion] = Entity, 460 | [FallingBlock] = Entity, 461 | [AreaEffectCloud] = Entity, 462 | [FishingFloat] = Entity, 463 | [Arrow] = Entity, 464 | [TippedArrow] = Arrow, 465 | [Boat] = Entity, 466 | [EnderCrystal] = Entity, 467 | [Fireball] = Entity, 468 | [WitherSkull] = Fireball, 469 | [Fireworks] = Entity, 470 | [Hanging] = Entity, 471 | [ItemFrame] = Hanging, 472 | [Item] = Entity, 473 | [Living] = Entity, 474 | [Player] = Living, 475 | [ArmorStand] = Living, 476 | [Insentinent] = Living, 477 | [Ambient] = Insentinent, 478 | [Bat] = Ambient, 479 | [Creature] = Insentinent, 480 | [Ageable] = Creature, 481 | [Animal] = Ageable, 482 | [Horse] = Animal, 483 | [Pig] = Animal, 484 | [Rabbit] = Animal, 485 | [PolarBear] = Animal, 486 | [Sheep] = Animal, 487 | [TameableAnimal] = Animal, 488 | [Ocelot] = TameableAnimal, 489 | [Wolf] = TameableAnimal, 490 | [Parrot] = TameableAnimal, 491 | [Villager] = Creature, 492 | [Golem] = Creature, 493 | [IronGolem] = Golem, 494 | [Snowman] = Golem, 495 | [Shulker] = Golem, 496 | [Monster] = Creature, 497 | [Blaze] = Monster, 498 | [Creeper] = Monster, 499 | [Guardian] = Monster, 500 | [Skeleton] = Monster, 501 | [Spider] = Monster, 502 | [CaveSpider] = Spider, 503 | [Witch] = Monster, 504 | [Wither] = Monster, 505 | [Zombie] = Monster, 506 | [ZombiePigman] = Zombie, 507 | [Enderman] = Monster, 508 | [Enderdragon] = Insentinent, 509 | [Flying] = Insentinent, 510 | [Ghast] = Flying, 511 | [Slime] = Insentinent, 512 | [MagmaCube] = Slime, 513 | [Minecart] = Entity, 514 | [MinecartCommandBlock] = Minecart, 515 | [MinecartFurnace] = Minecart, 516 | [ActivatedTNT] = Entity, 517 | 518 | //// Generic 519 | [Painting] = Entity, 520 | [ExperienceOrb] = Entity, 521 | }; 522 | 523 | #define ENUMNAME(name) [name] = #name 524 | 525 | const char * ENTITY_NAMES[MaxEntityType] = { 526 | // Generic 527 | ENUMNAME(Entity), 528 | ENUMNAME(Painting), 529 | ENUMNAME(ExperienceOrb), 530 | 531 | // Hostile mobs 532 | ENUMNAME(Creeper), 533 | ENUMNAME(Skeleton), 534 | ENUMNAME(Spider), 535 | ENUMNAME(GiantZombie), 536 | ENUMNAME(Zombie), 537 | ENUMNAME(Slime), 538 | ENUMNAME(Ghast), 539 | ENUMNAME(ZombiePigman), 540 | ENUMNAME(Enderman), 541 | ENUMNAME(CaveSpider), 542 | ENUMNAME(Silverfish), 543 | ENUMNAME(Blaze), 544 | ENUMNAME(MagmaCube), 545 | ENUMNAME(Enderdragon), 546 | ENUMNAME(Wither), 547 | ENUMNAME(Bat), 548 | ENUMNAME(Witch), 549 | ENUMNAME(Endermite), 550 | ENUMNAME(Guardian), 551 | ENUMNAME(Shulker), 552 | ENUMNAME(PolarBear), 553 | ENUMNAME(Husk), 554 | ENUMNAME(ZombieVillager), 555 | ENUMNAME(SkeletonHorse), 556 | ENUMNAME(ZombieHorse), 557 | ENUMNAME(EvocationIllager), 558 | ENUMNAME(Vex), 559 | ENUMNAME(VindicationIllager), 560 | ENUMNAME(IllusionIllager), 561 | 562 | // Passive mobs 563 | ENUMNAME(Pig), 564 | ENUMNAME(Sheep), 565 | ENUMNAME(Cow), 566 | ENUMNAME(Chicken), 567 | ENUMNAME(Squid), 568 | ENUMNAME(Wolf), 569 | ENUMNAME(Mooshroom), 570 | ENUMNAME(Snowman), 571 | ENUMNAME(Ocelot), 572 | ENUMNAME(IronGolem), 573 | ENUMNAME(Horse), 574 | ENUMNAME(Rabbit), 575 | ENUMNAME(Llama), 576 | ENUMNAME(Parrot), 577 | ENUMNAME(Villager), 578 | ENUMNAME(Donkey), 579 | ENUMNAME(Mule), 580 | 581 | // Objects 582 | ENUMNAME(Boat), 583 | ENUMNAME(ItemStack), 584 | ENUMNAME(AreaEffectCloud), 585 | ENUMNAME(Minecart), 586 | ENUMNAME(ChestMinecart), 587 | ENUMNAME(MinecartFurnace), 588 | ENUMNAME(MinecartCommandBlock), 589 | ENUMNAME(ActivatedTNT), 590 | ENUMNAME(EnderCrystal), 591 | ENUMNAME(FallingObjects), 592 | ENUMNAME(ItemFrame), 593 | ENUMNAME(LeashKnot), 594 | ENUMNAME(ArmorStand), 595 | 596 | // Projectiles 597 | ENUMNAME(Arrow), 598 | ENUMNAME(Snowball), 599 | ENUMNAME(Egg), 600 | ENUMNAME(Fireball), 601 | ENUMNAME(FireCharge), 602 | ENUMNAME(ThrownEnderpearl), 603 | ENUMNAME(WitherSkull), 604 | ENUMNAME(ShulkerBullet), 605 | ENUMNAME(EyeOfEnder), 606 | ENUMNAME(ThrownPotion), 607 | ENUMNAME(FallingDragonEgg), 608 | ENUMNAME(ThrownExpBottle), 609 | ENUMNAME(FireworkRocket), 610 | ENUMNAME(FishingFloat), 611 | ENUMNAME(SpectralArrow), 612 | ENUMNAME(TippedArrow), 613 | ENUMNAME(DragonFireball), 614 | ENUMNAME(LlamaSpit), 615 | ENUMNAME(EvocationFangs), 616 | }; 617 | 618 | const char * get_entity_name(char *buf, EntityType type) { 619 | if (type < 0 || type >= MaxEntityType ) { 620 | sprintf(buf, "%s", "IllegalEntityType"); 621 | } 622 | else if ( ENTITY_NAMES[type] ) { 623 | sprintf(buf, "%s", ENTITY_NAMES[type]); 624 | } 625 | else { 626 | sprintf(buf, "%s", "UnknownEntity"); 627 | } 628 | return buf; 629 | } 630 | 631 | //////////////////////////////////////////////////////////////////////////////// 632 | // Entity Metadata 633 | 634 | const char * METATYPES[] = { 635 | [META_BYTE] = "byte", 636 | [META_VARINT] = "varint", 637 | [META_FLOAT] = "float", 638 | [META_STRING] = "string", 639 | [META_CHAT] = "chat", 640 | [META_SLOT] = "slot", 641 | [META_BOOL] = "bool", 642 | [META_VEC3] = "vector3f", 643 | [META_POS] = "position", 644 | [META_OPTPOS] = "optional_position", 645 | [META_DIR] = "direction", 646 | [META_OPTUUID] = "optional_uuid", 647 | [META_BID] = "block_id", 648 | [META_NBT] = "nbt", 649 | [META_NONE] = "-" 650 | }; 651 | 652 | metadata * clone_metadata(metadata *meta) { 653 | if (!meta) return NULL; 654 | lh_create_num(metadata, newmeta, 32); 655 | memmove(newmeta, meta, 32*sizeof(metadata)); 656 | int i; 657 | for(i=0; i<32; i++) { 658 | switch (newmeta[i].type) { 659 | case META_SLOT: 660 | clone_slot(&meta[i].slot, &newmeta[i].slot); 661 | break; 662 | case META_NBT: 663 | newmeta[i].nbt = nbt_clone(meta[i].nbt); 664 | break; 665 | case META_STRING: 666 | case META_CHAT: 667 | newmeta[i].str = strdup(meta[i].str); 668 | break; 669 | } 670 | } 671 | return newmeta; 672 | } 673 | 674 | metadata * update_metadata(metadata *meta, metadata *upd) { 675 | if (!meta) return NULL; 676 | if (!upd) return meta; 677 | 678 | int i; 679 | for(i=0; i<32; i++) { 680 | if (upd[i].type != 0xff) { 681 | if (meta[i].type != upd[i].type) { 682 | printf("update_metadata : incompatible metadata types at index %d : old=%d vs new=%d\n", 683 | i, meta[i].type, upd[i].type); 684 | continue; 685 | } 686 | 687 | // replace stored metadata with the one from the packet 688 | switch (meta[i].type) { 689 | case META_SLOT: 690 | clear_slot(&meta[i].slot); 691 | clone_slot(&upd[i].slot, &meta[i].slot); 692 | break; 693 | case META_NBT: 694 | nbt_free(meta[i].nbt); 695 | meta[i].nbt = nbt_clone(upd[i].nbt); 696 | break; 697 | case META_STRING: 698 | case META_CHAT: 699 | lh_free(meta[i].str); 700 | meta[i].str = strdup(upd[i].str); 701 | break; 702 | default: 703 | meta[i] = upd[i]; 704 | } 705 | } 706 | } 707 | return meta; 708 | } 709 | 710 | 711 | void free_metadata(metadata *meta) { 712 | if (!meta) return; 713 | int i; 714 | for(i=0; i<32; i++) { 715 | switch (meta[i].type) { 716 | case META_SLOT: 717 | clear_slot(&meta[i].slot); 718 | break; 719 | case META_NBT: 720 | nbt_free(meta[i].nbt); 721 | break; 722 | case META_STRING: 723 | case META_CHAT: 724 | lh_free(meta[i].str); 725 | break; 726 | } 727 | } 728 | free(meta); 729 | } 730 | 731 | uint8_t * read_metadata(uint8_t *p, metadata **meta) { 732 | assert(meta); 733 | assert(!*meta); 734 | ssize_t mc = 0; 735 | 736 | // allocate a whole set of 32 values 737 | lh_alloc_num(*meta, 32); 738 | metadata *m = *meta; 739 | 740 | int i; 741 | int bool; 742 | 743 | // mark all entries as not present - we use the same 0xff value 744 | // that Mojang uses as terminator 745 | for(i=0; i<32; i++) m[i].type = META_NONE; 746 | 747 | char sbuf[MCP_MAXSTR]; 748 | 749 | while (1) { 750 | uint8_t key = read_char(p); 751 | if (key == 0xff) break; // terminator 752 | assert(key < 32); 753 | 754 | metadata *mm = &m[key]; 755 | mm->key = key; 756 | mm->type = read_char(p); 757 | 758 | switch (mm->type) { 759 | case META_BYTE: mm->b = read_char(p); break; 760 | case META_VARINT: mm->i = read_varint(p); break; 761 | case META_FLOAT: mm->f = read_float(p); break; 762 | case META_STRING: 763 | case META_CHAT: lh_free(mm->str); 764 | p = read_string(p,sbuf); 765 | mm->str = strdup(sbuf); 766 | break; 767 | case META_SLOT: clear_slot(&mm->slot); 768 | p = read_slot(p,&mm->slot); 769 | break; 770 | case META_BOOL: mm->bool = read_char(p); break; //VERIFY 771 | case META_VEC3: mm->fx=read_float(p); 772 | mm->fy=read_float(p); 773 | mm->fz=read_float(p); break; 774 | case META_POS: mm->pos.p = read_long(p); break; 775 | case META_OPTPOS: bool = read_char(p); //VERIFY 776 | if (bool) { 777 | mm->pos.p = read_long(p); 778 | } 779 | else { 780 | mm->pos.p = (uint64_t)-1; 781 | } 782 | break; 783 | case META_DIR: mm->dir = read_varint(p); break; 784 | case META_OPTUUID: bool = read_char(p); //VERIFY 785 | if (bool) { 786 | memmove(mm->uuid,p,sizeof(uuid_t)); 787 | p+=sizeof(uuid_t); 788 | } 789 | break; 790 | case META_BID: mm->block = read_char(p); break; // note- block ID only, no meta 791 | case META_NBT: nbt_free(mm->nbt); 792 | mm->nbt = nbt_parse(&p); 793 | break; 794 | } 795 | } 796 | 797 | return p; 798 | } 799 | 800 | uint8_t * write_metadata(uint8_t *w, metadata *meta) { 801 | assert(meta); 802 | 803 | int i,j; 804 | char bool; 805 | for (i=0; i<32; i++) { 806 | metadata *mm = meta+i; 807 | if (mm->type==META_NONE) continue; 808 | 809 | lh_write_char(w, mm->key); 810 | lh_write_char(w, mm->type); 811 | switch (mm->type) { 812 | case META_BYTE: write_char(w, mm->b); break; 813 | case META_VARINT: write_varint(w, mm->i); break; 814 | case META_FLOAT: write_float(w, mm->f); break; 815 | case META_STRING: 816 | case META_CHAT: w = write_string(w, mm->str); break; 817 | case META_SLOT: w = write_slot(w, &mm->slot); break; 818 | case META_BOOL: write_char(w, mm->bool); break; 819 | case META_VEC3: write_float(w, mm->fx); 820 | write_float(w, mm->fy); 821 | write_float(w, mm->fz); break; 822 | case META_POS: write_long(w, mm->pos.p); break; 823 | case META_OPTPOS: bool = (mm->pos.p == -1); 824 | write_char(w, bool); 825 | if (bool) { 826 | write_long(w, mm->pos.p); 827 | } 828 | break; 829 | case META_DIR: write_varint(w, mm->dir); break; 830 | case META_OPTUUID: bool = 0; 831 | for(j=0; j<16; j++) 832 | if (mm->uuid[j]) 833 | bool = 1; 834 | write_char(w, bool); 835 | if (bool) { 836 | memmove(w, mm->uuid, sizeof(mm->uuid)); 837 | w+=16; 838 | } 839 | break; 840 | case META_BID: write_char(w, mm->block); break; 841 | case META_NBT: nbt_write(&w, mm->nbt); break; 842 | } 843 | } 844 | lh_write_char(w, 0xff); 845 | 846 | return w; 847 | } 848 | 849 | void dump_metadata(metadata *meta, EntityType et) { 850 | if (!meta) return; 851 | 852 | int i; 853 | for (i=0; i<32; i++) { 854 | metadata *mm = meta+i; 855 | if (mm->type==META_NONE) continue; 856 | 857 | printf("\n "); 858 | 859 | const char * name = NULL; 860 | EntityType ett = et; 861 | while ((!name) && (ett!=IllegalEntityType)) { 862 | name = currentProtocolkey] : METANAME[ett][mm->key]; 863 | ett = ENTITY_HIERARCHY[ett]; 864 | } 865 | 866 | printf("%2d %-24s [%-6s] = ", mm->key, name?name:"Unknown",METATYPES[mm->type]); 867 | switch (mm->type) { 868 | case META_BYTE: printf("%d", mm->b); break; 869 | case META_VARINT: printf("%d", mm->i); break; 870 | case META_FLOAT: printf("%.1f",mm->f); break; 871 | case META_STRING: 872 | case META_CHAT: printf("\"%s\"", mm->str); break; 873 | case META_SLOT: dump_slot(&mm->slot); break; 874 | case META_BOOL: printf("%s", mm->bool?"true":"false"); break; //VERIFY 875 | case META_VEC3: printf("%.1f,%.1f,%.1f", mm->fx, mm->fy, mm->fz); break; 876 | case META_POS: 877 | case META_OPTPOS: printf("%d,%d,%d", mm->pos.x, mm->pos.y, mm->pos.z); break; 878 | case META_DIR: printf("%d",mm->dir); break; 879 | case META_OPTUUID: hexprint(mm->uuid, sizeof(uuid_t)); break; 880 | case META_BID: printf("%2x (%d)", mm->block, mm->block); break; //TODO: print material name 881 | case META_NBT: printf("NBT data %p", mm->nbt); break; 882 | default: printf(""); break; 883 | } 884 | } 885 | } 886 | 887 | -------------------------------------------------------------------------------- /entity.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2016 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | /* 14 | entity : entities and entity metadata handling 15 | */ 16 | 17 | #include 18 | 19 | #include "mcp_types.h" 20 | #include "slot.h" 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | 24 | typedef enum { 25 | //// Superclasses 26 | IllegalEntityType = -1, 27 | Entity = 0, 28 | Living = 1, 29 | Ageable = 2, 30 | Player = 3, 31 | 32 | ElderGuardian = 4, 33 | WitherSkeleton = 5, 34 | Stray = 6, 35 | 36 | Potion = 7, 37 | FallingBlock = 8, 38 | Hanging = 9, 39 | Insentinent = 10, 40 | Ambient = 11, 41 | Creature = 12, 42 | Animal = 13, 43 | Golem = 14, 44 | Flying = 15, 45 | 46 | Husk = 23, 47 | ZombieVillager = 27, 48 | SkeletonHorse = 28, 49 | ZombieHorse = 29, 50 | Donkey = 31, 51 | Mule = 32, 52 | EvocationFangs = 33, 53 | EvocationIllager = 34, 54 | Vex = 35, 55 | VindicationIllager = 36, 56 | IllusionIllager = 37, 57 | 58 | Mob = 48, 59 | Monster = 49, 60 | 61 | 62 | //// Mobs 63 | Creeper = 50, 64 | Skeleton = 51, 65 | Spider = 52, 66 | GiantZombie = 53, 67 | Zombie = 54, 68 | Slime = 55, 69 | Ghast = 56, 70 | ZombiePigman = 57, 71 | Enderman = 58, 72 | CaveSpider = 59, 73 | 74 | Silverfish = 60, 75 | Blaze = 61, 76 | MagmaCube = 62, 77 | Enderdragon = 63, 78 | Wither = 64, 79 | Bat = 65, 80 | Witch = 66, 81 | Endermite = 67, 82 | Guardian = 68, 83 | Shulker = 69, 84 | 85 | Pig = 90, 86 | Sheep = 91, 87 | Cow = 92, 88 | Chicken = 93, 89 | Squid = 94, 90 | Wolf = 95, 91 | Mooshroom = 96, 92 | Snowman = 97, 93 | Ocelot = 98, 94 | IronGolem = 99, 95 | 96 | Horse = 100, 97 | Rabbit = 101, 98 | PolarBear = 102, 99 | Llama = 103, 100 | LlamaSpit = 104, 101 | Parrot = 105, 102 | 103 | Villager = 120, 104 | 105 | // inofficial entity types so we can track exp orbs and paintings 106 | TameableAnimal = 244, 107 | Item = 245, 108 | Fireworks = 246, 109 | 110 | Painting = 254, 111 | ExperienceOrb = 255, 112 | 113 | //// Objects 114 | Boat = 1 +256, 115 | ItemStack = 2 +256, 116 | AreaEffectCloud = 3 +256, 117 | 118 | Minecart = 10+256, 119 | ChestMinecart = 11+256, // deprecated since 1.6 120 | MinecartFurnace = 12+256, // deprecated since 1.6 121 | MinecartCommandBlock= 13+256, 122 | 123 | ActivatedTNT = 50+256, 124 | EnderCrystal = 51+256, 125 | 126 | Arrow = 60+256, 127 | Snowball = 61+256, 128 | Egg = 62+256, 129 | Fireball = 63+256, 130 | FireCharge = 64+256, 131 | ThrownEnderpearl = 65+256, 132 | WitherSkull = 66+256, 133 | ShulkerBullet = 67+256, 134 | 135 | FallingObjects = 70+256, 136 | ItemFrame = 71+256, 137 | EyeOfEnder = 72+256, 138 | ThrownPotion = 73+256, 139 | FallingDragonEgg = 74+256, 140 | ThrownExpBottle = 75+256, 141 | FireworkRocket = 76+256, 142 | LeashKnot = 77+256, 143 | ArmorStand = 78+256, 144 | 145 | FishingFloat = 90+256, 146 | SpectralArrow = 91+256, 147 | TippedArrow = 92+256, 148 | DragonFireball = 93+256, 149 | 150 | //// Terminating ID 151 | MaxEntityType = 512, 152 | } EntityType; 153 | 154 | extern const char * METANAME[][32]; 155 | extern const EntityType ENTITY_HIERARCHY[]; 156 | extern const char * ENTITY_NAMES[]; 157 | const char * get_entity_name(char *buf, EntityType type); 158 | 159 | //////////////////////////////////////////////////////////////////////////////// 160 | 161 | #define META_BYTE 0 162 | #define META_VARINT 1 163 | #define META_FLOAT 2 164 | #define META_STRING 3 165 | #define META_CHAT 4 166 | #define META_SLOT 5 167 | #define META_BOOL 6 168 | #define META_VEC3 7 169 | #define META_POS 8 170 | #define META_OPTPOS 9 171 | #define META_DIR 10 172 | #define META_OPTUUID 11 173 | #define META_BID 12 174 | #define META_NBT 13 175 | #define META_NONE 255 176 | 177 | // single metadata key-value pair 178 | typedef struct { 179 | uint8_t key; 180 | uint8_t type; 181 | union { 182 | uint8_t b; 183 | int32_t i; 184 | float f; 185 | char *str; 186 | slot_t slot; 187 | uint8_t bool; 188 | struct { 189 | float fx; 190 | float fy; 191 | float fz; 192 | }; 193 | pos_t pos; // OPTPOS with missing position is encoded as (int64_t)-1 194 | int32_t dir; 195 | uuid_t uuid; // missing UUID is encoded in all-zeros 196 | uint8_t block; // block ID only 197 | nbt_t* nbt; 198 | }; 199 | } metadata; 200 | 201 | extern const char * METATYPES[]; 202 | 203 | metadata * clone_metadata(metadata *meta); 204 | metadata * update_metadata(metadata *meta, metadata *upd); 205 | void free_metadata(metadata *meta); 206 | uint8_t * read_metadata(uint8_t *p, metadata **meta); 207 | uint8_t * write_metadata(uint8_t *w, metadata *meta); 208 | void dump_metadata(metadata *meta, EntityType et); 209 | 210 | -------------------------------------------------------------------------------- /helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "helpers.h" 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | // Time 21 | 22 | uint64_t gettimestamp() { 23 | struct timeval tv; 24 | gettimeofday(&tv, NULL); 25 | uint64_t ts = (uint64_t)tv.tv_sec*1000000+(uint64_t)tv.tv_usec; 26 | return ts; 27 | } 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | // Token bucket rate-limiter 31 | 32 | tokenbucket * tb_init(tokenbucket *tb, int64_t interval, int64_t burst) { 33 | if (!tb) tb = (tokenbucket *)malloc(sizeof(tokenbucket)); 34 | assert(tb); 35 | 36 | tb->last = gettimestamp(); 37 | tb->level = burst; 38 | tb->interval = interval; 39 | tb->burst = burst; 40 | 41 | return tb; 42 | } 43 | 44 | int tb_event(tokenbucket *tb, uint64_t size) { 45 | uint64_t ts = gettimestamp(); 46 | uint64_t level = tb->level + (ts-tb->last)/tb->interval; // currently available token level 47 | if (level > tb->burst) level = tb->burst; 48 | 49 | if (size > level) return 0; // disallow this event 50 | 51 | tb->level = level-size; 52 | tb->last = ts; 53 | return size; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // Useful macros 17 | 18 | #ifndef MIN 19 | #define MIN(a,b) ((a)<(b)?(a):(b)) 20 | #define MAX(a,b) ((a)>(b)?(a):(b)) 21 | #endif 22 | #define SGN(x) ((x)?(((x)>0)?1:-1):0) 23 | #define SQ(x) ((x)*(x)) 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // Time 27 | 28 | uint64_t gettimestamp(); 29 | 30 | //////////////////////////////////////////////////////////////////////////////// 31 | // Tocken bucket rate-limiter 32 | 33 | typedef struct { 34 | uint64_t last; // last timestamp when an event was allowed by rate-limiting 35 | uint64_t level; // current number of tokens 36 | uint64_t interval; // average interval, in us 37 | uint64_t burst; // burst size, in tokens 38 | } tokenbucket; 39 | 40 | #define TBDEF(name, i, b) \ 41 | tokenbucket name = { .last=0, .level=b, .interval=i, .burst=b }; 42 | 43 | tokenbucket * tb_init(tokenbucket *tb, int64_t interval, int64_t burst); 44 | int tb_event(tokenbucket *tb, uint64_t size); 45 | -------------------------------------------------------------------------------- /hud.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "mcp_packet.h" 14 | 15 | #define HUDINV_NONE 0LL 16 | #define HUDINV_ANY (HUDINV_NONE-1) 17 | #define HUDINV_POSITION (1LL<<1) 18 | #define HUDINV_HEALTH (1LL<<2) 19 | #define HUDINV_INVENTORY (1LL<<3) 20 | #define HUDINV_ENTITIES (1LL<<4) 21 | #define HUDINV_BLOCKS (1LL<<5) 22 | #define HUDINV_BUILD (1LL<<6) 23 | #define HUDINV_HELP (1LL<<7) 24 | 25 | int hud_bogus_map(slot_t *s); 26 | void hud_cmd(char **words, MCPacketQueue *sq, MCPacketQueue *cq); 27 | void hud_renew(MCPacketQueue *cq); 28 | void hud_update(MCPacketQueue *cq); 29 | void hud_invalidate(uint64_t flags); 30 | -------------------------------------------------------------------------------- /hudfonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/hudfonts.png -------------------------------------------------------------------------------- /mapper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | typedef struct { 11 | uint8_t r,g,b; 12 | uint16_t bid; 13 | uint8_t meta; 14 | int support; 15 | } mapmat_t; 16 | 17 | mapmat_t COLORS[] = { 18 | { 127, 178, 56, 0x02, 0, 0 }, // Grass 19 | { 247, 233, 163, 0x2c, 1, 2 }, // Sandstone slab 20 | //{ 167, 167, 167, 0x64, 0, 0 }, // Mushroom block 21 | { 255, 0, 0, 0x98, 0, 0 }, // Redstone block 22 | { 160, 160, 255, 0xae, 0, 0 }, // Packed ice 23 | { 167, 167, 167, 0x94, 0, 1 }, // Heavy (iron) pressure plate 24 | { 0, 124, 0, 0x12, 0, 0 }, // Oak leaves 25 | { 255, 255, 255, 0xab, 0, 1 }, // White carpet 26 | { 164, 168, 184, 0x52, 0, 0 }, // Clay block 27 | { 183, 106, 47, 0x7e, 3, 0 }, // Jungle wooden slab 28 | { 112, 112, 112, 0x2c, 3, 2 }, // Cobblestone slab 29 | { 64, 64, 255, 0x09, 0, 1 }, // Water 30 | { 104, 83, 50, 0x7e, 0, 2 }, // Oak wooden slab 31 | { 255, 252, 245, 0x01, 3, 0 }, // Diorite 32 | { 216, 127, 51, 0xab, 1, 1 }, // Orange carpet 33 | { 178, 76, 216, 0xab, 2, 1 }, // Magenta carpet 34 | { 102, 153, 216, 0xab, 3, 1 }, // L-blue carpet 35 | { 229, 229, 51, 0xab, 4, 1 }, // Yellow carpet 36 | { 127, 204, 25, 0xab, 5, 1 }, // Lime carpet 37 | { 242, 127, 165, 0xab, 6, 1 }, // Pink carpet 38 | { 76, 76, 76, 0xab, 7, 1 }, // Gray carpet 39 | { 153, 153, 153, 0xab, 8, 1 }, // L-gray carpet 40 | { 76, 127, 153, 0xab, 9, 1 }, // Cyan carpet 41 | { 127, 63, 178, 0xab, 10, 1 }, // Purple carpet 42 | { 51, 76, 178, 0xab, 11, 1 }, // Blue carpet 43 | { 102, 76, 51, 0xab, 12, 1 }, // Brown carpet 44 | { 102, 127, 51, 0xab, 13, 1 }, // Green carpet 45 | { 153, 51, 51, 0xab, 14, 1 }, // Red carpet 46 | { 25, 25, 25, 0xab, 15, 1 }, // Black carpet 47 | { 250, 238, 77, 0x93, 0, 1 }, // Light (gold) pressure plate 48 | { 92, 219, 213, 0xa8, 1, 0 }, // Prismarine Bricks 49 | { 74, 128, 255, 0x16, 0, 0 }, // Lapis block 50 | { 0, 217, 58, 0x85, 0, 0 }, // Emerald block 51 | { 21, 20, 31, 0x7e, 1, 2 }, // Spruce wooden slab 52 | { 112, 2, 0, 0x57, 0, 0 }, // Netherrack 53 | }; 54 | 55 | float color_diff(uint8_t r, uint8_t g, uint8_t b, int idx, int subidx) { 56 | mapmat_t cd = COLORS[idx]; 57 | 58 | float f; 59 | switch(subidx) { 60 | case 0: f=180.0/255.0; break; 61 | case 1: f=220.0/255.0; break; 62 | case 2: f=1.0; break; 63 | case 3: f=135.0/255.0; break; 64 | } 65 | float dr = (float)COLORS[idx].r*f-r; 66 | float dg = (float)COLORS[idx].g*f-g; 67 | float db = (float)COLORS[idx].b*f-b; 68 | return sqrtf(dr*dr+dg*dg+db*db); 69 | } 70 | 71 | int pick_nearest_color(uint8_t r, uint8_t g, uint8_t b, int *level, int flat) { 72 | int i; 73 | 74 | float diff = 1000; 75 | int idx = -1; *level=0; 76 | 77 | for(i=0; COLORS[i].bid; i++) { 78 | float d_level, d_low, d_high; 79 | d_level = color_diff(r,g,b,i,1); 80 | if (!flat) { 81 | d_low = color_diff(r,g,b,i,0); 82 | d_high = color_diff(r,g,b,i,2); 83 | } 84 | 85 | if (d_levelwidth; i++) { 100 | uint32_t color = IMGDOT(img, col, i); 101 | int idx = pick_nearest_color((color>>16)&0xff, (color>>8)&0xff, color&0xff, &level, flat); 102 | int needsupport = COLORS[idx].support; 103 | 104 | if ((level<0 && prev>0) || (level>0 && prev<0)) { 105 | clevel=0; 106 | } 107 | else { 108 | clevel += level; 109 | } 110 | fprintf(out,"%d,%d,%d,%d,%d\n", col, clevel, i-(img->height-1), COLORS[idx].bid, COLORS[idx].meta); 111 | 112 | // depending on block placement we may need to add some supports 113 | switch (level) { 114 | case 0: 115 | if (needsupport==1) { 116 | // support for ourselves 117 | fprintf(out,"%d,%d,%d,%d,%d\n", col, clevel-1, i-(img->height-1), 3, 0); 118 | // support for the previous block 119 | if (!prevsupport) 120 | fprintf(out,"%d,%d,%d,%d,%d\n", col, prev-1, i-(img->height-1)-1, 3, 0); 121 | prevsupport = 1; 122 | } 123 | break; 124 | case -1: // current block is lower than the previous one 125 | // in any case we need a support for the previous one, so we can connect 126 | if (!prevsupport) 127 | fprintf(out,"%d,%d,%d,%d,%d\n", col, prev-1, i-(img->height-1)-1, 3, 0); 128 | if (needsupport==1) { 129 | // support for ourselves 130 | fprintf(out,"%d,%d,%d,%d,%d\n", col, clevel-1, i-(img->height-1), 3, 0); 131 | // second support for the previous block - so we can connect our support to it 132 | fprintf(out,"%d,%d,%d,%d,%d\n", col, prev-2, i-(img->height-1)-1, 3, 0); 133 | prevsupport = 1; 134 | } 135 | break; 136 | case 1: // current block is higher than the previous one 137 | // support for ourselves in any case 138 | fprintf(out,"%d,%d,%d,%d,%d\n", col, clevel-1, i-(img->height-1), 3, 0); 139 | prevsupport = 1; 140 | break; 141 | } 142 | prev = clevel; 143 | } 144 | } 145 | 146 | int main(int ac, char **av) { 147 | if (!av[1] || !av[2]) { 148 | printf("Usage: %s \n", av[0]); 149 | exit(1); 150 | } 151 | 152 | lhimage *img = import_png_file(av[1]); 153 | if (!img) { 154 | printf("Failed to load PNG image from %s\n", av[1]); 155 | exit(2); 156 | } 157 | 158 | int col; 159 | FILE *out = fopen(av[2], "w"); 160 | if (!out) { 161 | printf("Failed to open file %s for writing: %s\n", av[2], strerror(errno)); 162 | exit(2); 163 | } 164 | fprintf(out, "x,y,z,bid,meta\n"); 165 | 166 | for(col=0; colwidth; col++) 167 | process_column(img, col, 1, out); 168 | 169 | fclose(out); 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /mcp_arg.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "mcp_arg.h" 19 | #include "mcp_ids.h" 20 | 21 | // count the number of format specs in a string 22 | // this is needed by mcparg_parse to check if the format string 23 | // was correctly matched by sscanf with all arguments 24 | static inline int count_fmt(const char *fmt) { 25 | int i, c=0; 26 | for(i=0; fmt[i] && fmt[i+1]; i++) 27 | if (fmt[i] == '%') 28 | if (fmt[++i]!='%') c++; // skip %% 29 | 30 | return c; 31 | } 32 | 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | // parse the arguments using the provided options spec 37 | // words : tokenized commandline 38 | // names : possible names for the option 39 | // fmt : possible format strings for the option 40 | int argparse(char **words, char **names, char **fmt, ...) { 41 | int i,j; 42 | char *value = NULL; int ni; // the pointer (to value) and the index of a named option 43 | char *unvalue = NULL; int ui; // the pointer and the index of the first unnamed option 44 | 45 | // locate the option in the words and extract the value 46 | for(i=0; words[i] && !value; i++) { 47 | if (words[i][0] == '-') continue; // skip flag-like options 48 | 49 | char *eq = index(words[i], '='); 50 | if (!eq && !unvalue) { 51 | // this looks like an unnamed option 52 | unvalue = words[i]; 53 | ui = i; 54 | } 55 | else { 56 | // named option 57 | for(j=0; names[j]; j++) { 58 | int nlen = eq-words[i]; 59 | if (!strncmp(words[i],names[j],nlen)) { 60 | // we have a matching name 61 | value = eq+1; 62 | ni = i; 63 | break; 64 | } 65 | } 66 | } 67 | } 68 | 69 | // check if we found the named option and if not if can use an unnamed one 70 | if (!value) { 71 | if (unvalue) { 72 | value = unvalue; 73 | ni = ui; 74 | } 75 | else 76 | return MCPARG_NOT_FOUND; // nothing found 77 | } 78 | 79 | va_list args; 80 | 81 | // try to parse the value using all offered format options 82 | for(i=0; fmt[i]; i++) { 83 | int nf = count_fmt(fmt[i]); 84 | va_start (args, fmt); 85 | if (vsscanf(value, fmt[i], args) == nf) { 86 | // scanf could correctly parse all arguments in this spec 87 | va_end (args); 88 | 89 | // remove the successfully parsed option from the list 90 | for(j=ni; words[j]; j++) 91 | words[j] = words[j+1]; 92 | 93 | // return the index of the format that matched 94 | return i; 95 | } 96 | va_end (args); 97 | } 98 | 99 | // none of the format specs matched - likely incorrect formatting 100 | return MCPARG_NOT_PARSED; 101 | } 102 | 103 | // parse the arguments using the provided options spec 104 | // words : tokenized commandline 105 | // names : possible names for the option 106 | // fmt : possible format strings for the option 107 | int argflag(char **words, char **names) { 108 | int i,j; 109 | 110 | // locate the option in the words and extract the value 111 | for(i=0; words[i]; i++) { 112 | if (words[i][0] == '-') { 113 | char *name = words[i]+1; 114 | for(j=0; names[j]; j++) { 115 | if (!strcmp(names[j], name)) { 116 | // remove the successfully parsed option from the list 117 | for(; words[i]; i++) 118 | words[i] = words[i+1]; 119 | return 1; 120 | } 121 | } 122 | } 123 | } 124 | return 0; 125 | } 126 | 127 | //////////////////// 128 | 129 | int argf_size(arg_defaults *ad, char **words, char **names, size3_t *sz) { 130 | // default name list 131 | if (!names) names = WORDLIST("size","sz","s"); 132 | 133 | // possible option formats 134 | char ** fmt_size = WORDLIST("%d,%d,%d", 135 | "%d,%d", 136 | "%d"); 137 | 138 | int fi = argparse(words, names, fmt_size, &sz->x, &sz->z, &sz->y); 139 | switch (fi) { 140 | case 0: 141 | break; 142 | case 1: 143 | sz->y=1; 144 | break; 145 | case 2: 146 | sz->y=1; 147 | sz->z=sz->x; 148 | break; 149 | 150 | case MCPARG_NOT_FOUND: 151 | case MCPARG_NOT_PARSED: 152 | return fi; 153 | default: 154 | assert(0); 155 | } 156 | 157 | return 0; 158 | } 159 | 160 | const char *argfmt_size = "size=x[,z[,y]]"; 161 | 162 | //////////////////// 163 | 164 | int argf_diam(arg_defaults *ad, char **words, char **names, float *diam) { 165 | // default name list 166 | if (!names) names = WORDLIST("diameter","diam","d"); 167 | 168 | // possible option formats 169 | char ** fmt_diam = WORDLIST("%f"); 170 | 171 | int fi = argparse(words, names, fmt_diam, diam); 172 | switch (fi) { 173 | case 0: 174 | break; 175 | 176 | case MCPARG_NOT_FOUND: 177 | case MCPARG_NOT_PARSED: 178 | return fi; 179 | default: 180 | assert(0); 181 | } 182 | 183 | return 0; 184 | } 185 | 186 | const char *argfmt_diam = "diam=d"; 187 | 188 | //////////////////// 189 | 190 | int argf_pivot(arg_defaults *ad, char **words, char **names, pivot_t *pivot) { 191 | // default name list 192 | if (!names) names = WORDLIST("pivot","pv","p","from","pos","at"); 193 | 194 | // possible option formats 195 | char ** fmt_pivot = WORDLIST("%d,%d,%d,%4$[NEWSnews]", 196 | "%d,%d,%d", 197 | "%d,%d,%4$[NEWSnews]", 198 | "%d,%d", 199 | "%5$d"); 200 | 201 | char dir[256]; 202 | int32_t dist; 203 | 204 | int fi = argparse(words, names, fmt_pivot, &pivot->pos.x, &pivot->pos.z, &pivot->pos.y, dir, &dist); 205 | switch (fi) { 206 | case 0: // explicitly specified coordinates and direction 207 | pivot->dir = ltodir(dir[0]); 208 | break; 209 | case 1: // only coordinates specified - use player's direction as pivot direction 210 | pivot->dir = ad->pd; 211 | break; 212 | case 2: // x,z coordinate and direction - use player's y coordinate 213 | pivot->dir = ltodir(dir[0]); 214 | pivot->pos.y = ad->py; 215 | break; 216 | case 3: // x,z coordinate only - use player's y and direction 217 | pivot->pos.y = ad->py; 218 | pivot->dir = ad->pd; 219 | break; 220 | case 4: // only single number specified - assume it's a distance from player's position 221 | pivot->pos.y = ad->py; 222 | pivot->dir = ad->pd; 223 | switch(pivot->dir) { 224 | case DIR_NORTH: 225 | pivot->pos.x = ad->px; 226 | pivot->pos.z = ad->pz-dist; 227 | break; 228 | case DIR_SOUTH: 229 | pivot->pos.x = ad->px; 230 | pivot->pos.z = ad->pz+dist; 231 | break; 232 | case DIR_EAST: 233 | pivot->pos.x = ad->px+dist; 234 | pivot->pos.z = ad->pz; 235 | break; 236 | case DIR_WEST: 237 | pivot->pos.x = ad->px-dist; 238 | pivot->pos.z = ad->pz; 239 | break; 240 | } 241 | break; 242 | 243 | case MCPARG_NOT_FOUND: 244 | case MCPARG_NOT_PARSED: 245 | return fi; 246 | default: 247 | assert(0); 248 | } 249 | 250 | return 0; 251 | } 252 | 253 | const char *argfmt_pivot = "pivot=distance or pivot=x,z[,y[,dir]]"; 254 | 255 | //////////////////// 256 | 257 | int argf_offset(arg_defaults *ad, char **words, char **names, off3_t *offset) { 258 | // default name list 259 | if (!names) names = WORDLIST("offset","off","o"); 260 | 261 | // possible option formats 262 | char ** fmt_offset = WORDLIST("%d,%d,%d", 263 | "%d,%d", 264 | "%4$d%5$[RLFBUDrlfbud]", 265 | "%d", 266 | "%5$[RLFBUDrlfbud]"); 267 | 268 | int32_t x,y,z,o; 269 | char dir[4096]; dir[0] = 0; 270 | 271 | int fi = argparse(words, names, fmt_offset, &x, &z, &y, &o, dir); 272 | switch (fi) { 273 | case 0: // explicitly specified offset 274 | break; 275 | case 1: // x,z offset - assume y=0 276 | y = 0; 277 | break; 278 | case 2: // explicit offset and direction 279 | switch(tolower(dir[0])) { 280 | case 'u': x=0; z=0; y=o; break; 281 | case 'd': x=0; z=0; y=-o; break; 282 | case 'r': x=o; z=0; y=0; break; 283 | case 'l': x=-o; z=0; y=0; break; 284 | case 'f': x=0; z=-o; y=0; break; 285 | case 'b': x=0; z=o; y=0; break; 286 | } 287 | break; 288 | case 3: // x-only offset - assume z=y=0 289 | y = 0; 290 | z = 0; 291 | break; 292 | case 4: // direction only - calculate offset from the buildplan size 293 | switch(tolower(dir[0])) { 294 | case 'u': x=0; z=0; y=ad->bpsy; break; 295 | case 'd': x=0; z=0; y=-ad->bpsy; break; 296 | case 'r': x=ad->bpsx; z=0; y=0; break; 297 | case 'l': x=-ad->bpsx; z=0; y=0; break; 298 | case 'f': x=0; z=-ad->bpsz; y=0; break; 299 | case 'b': x=0; z=ad->bpsz; y=0; break; 300 | } 301 | break; 302 | case MCPARG_NOT_FOUND: 303 | case MCPARG_NOT_PARSED: 304 | return fi; 305 | default: 306 | assert(0); 307 | } 308 | offset->x = x; 309 | offset->y = y; 310 | offset->z = z; 311 | 312 | return 0; 313 | } 314 | 315 | const char *argfmt_offset = "offset=x[,z[,y]] or offset=[n]direction"; 316 | 317 | //////////////////////////////////////////////////////////////////////////////// 318 | 319 | int argf_pos(arg_defaults *ad, char **words, char **names, off3_t *pos) { 320 | // default name list 321 | if (!names) names = WORDLIST("position","pos","p"); 322 | 323 | // possible option formats 324 | char ** fmt_pos = WORDLIST("%d,%d,%d", 325 | "%d,%d", 326 | "%4$d"); 327 | 328 | int32_t dist; 329 | 330 | int fi = argparse(words, names, fmt_pos, &pos->x, &pos->z, &pos->y, &dist); 331 | switch (fi) { 332 | case 0: // explicit x,z,y coordinate 333 | break; 334 | case 1: // x,z coordinate - use player's y coordinate 335 | pos->y = ad->py; 336 | break; 337 | case 2: // only single number specified - assume it's a distance from player's position 338 | pos->y = ad->py; 339 | switch(ad->pd) { 340 | case DIR_NORTH: 341 | pos->x = ad->px; 342 | pos->z = ad->pz-dist; 343 | break; 344 | case DIR_SOUTH: 345 | pos->x = ad->px; 346 | pos->z = ad->pz+dist; 347 | break; 348 | case DIR_EAST: 349 | pos->x = ad->px+dist; 350 | pos->z = ad->pz; 351 | break; 352 | case DIR_WEST: 353 | pos->x = ad->px-dist; 354 | pos->z = ad->pz; 355 | break; 356 | } 357 | break; 358 | 359 | case MCPARG_NOT_FOUND: 360 | case MCPARG_NOT_PARSED: 361 | return fi; 362 | default: 363 | assert(0); 364 | } 365 | 366 | return 0; 367 | } 368 | 369 | const char *argfmt_pos = "pos=distance or pos=x,z[,y]"; 370 | 371 | //////////////////// 372 | 373 | int argf_mat(arg_defaults *ad, char **words, char **names, bid_t *mat) { 374 | // default name list 375 | if (!names) names = WORDLIST("material","mat","m"); 376 | 377 | // possible option formats 378 | char ** fmt_mat = WORDLIST("0x%x:%d%5$[^0-9]", // 0: expl. hex BID+meta+opt 0x2c:1u 379 | "0x%x:%d", // 1: expl. hex BID+meta 0x2c:9 380 | "%d:%d%5$[^0-9]", // 2: BID+meta+opt 44:1u 381 | "%d:%d", // 3: BID+meta 44:9 382 | "0x%x:%5$[^0-9]", // 4: hex BID 0x2c:u 383 | "0x%x", // 5: hex BID 0x2c 384 | "%d%5$[^0-9]", // 6: BID 44u 385 | "%d", // 7: BID 44 386 | "%3$[^:]:%2$d%5$[^0-9]", // 8: bname+meta+opt stone_slab:1u 387 | "%3$[^:]:%2$d", // 9: bname+meta stone_slab:9 388 | "%3$[^:]:%4$[^:]:%5$s", // 10: bname+mname+opt stone_slab:sandstone:u 389 | "%3$[^:]:%4$[^:]", // 11: bname+mname stone_slab:sandstone 390 | "%3$[^:]::%5$[^0-9:]", // 12: bname+opt stone_slab::u 391 | "%3$s"); // 13: bname stone_slab 392 | 393 | // try to locate and parse one of the formats for material spec 394 | int bid=-1, meta=0; 395 | char sbid[4096], smeta[4096], sopt[4096]; 396 | sbid[0]=0; smeta[0]=0; sopt[0]=0; 397 | 398 | int fi = argparse(words, names, fmt_mat, &bid, &meta, sbid, smeta, sopt); 399 | switch (fi) { 400 | case 0: 401 | case 1: 402 | case 2: 403 | case 3: 404 | case 4: 405 | case 5: 406 | case 6: 407 | case 7: 408 | break; 409 | case 8: 410 | case 9: 411 | case 12: 412 | case 13: 413 | bid = find_bid_name(sbid); 414 | if (bid<0) { 415 | printf("Could not find material name %s\n", sbid); 416 | return MCPARG_LOOKUP_FAILED; 417 | } 418 | break; 419 | case 10: 420 | case 11: 421 | bid = find_bid_name(sbid); 422 | if (bid<0) { 423 | printf("Could not find material name %s\n", sbid); 424 | return MCPARG_LOOKUP_FAILED; 425 | } 426 | meta = find_meta_name(bid, smeta); 427 | if (meta<0) { 428 | printf("Could not find meta name %s\n", smeta); 429 | return MCPARG_LOOKUP_FAILED; 430 | } 431 | break; 432 | case MCPARG_NOT_FOUND: 433 | case MCPARG_NOT_PARSED: 434 | return fi; 435 | default: 436 | assert(0); 437 | } 438 | 439 | if (sopt[0]) { 440 | // additional option specified 441 | if (ITEMS[bid].flags&I_SLAB) { 442 | if (sopt[0]=='u' || sopt[0]=='h') 443 | meta |= 8; 444 | else if (sopt[0]=='l') 445 | meta &= 7; 446 | } 447 | //TODO: add options for other block types 448 | } 449 | 450 | *mat = BLOCKTYPE(bid, meta); 451 | 452 | char buf[256]; 453 | 454 | return 0; 455 | } 456 | 457 | const char *argfmt_mat = "mat=material[:meta][upper]"; 458 | 459 | //////////////////// 460 | 461 | int argf_dir(arg_defaults *ad, char **words, char **names, int *dir) { 462 | // default name list 463 | if (!names) names = WORDLIST("direction","dir","d"); 464 | 465 | // possible option formats 466 | char ** fmt_dir = WORDLIST("%[NEWSnews]"); 467 | 468 | char dirs[4096]; dirs[0] = 0; 469 | 470 | int fi = argparse(words, names, fmt_dir, dirs); 471 | switch (fi) { 472 | case 0: { 473 | switch(tolower(dirs[0])) { 474 | case 'n': *dir=DIR_NORTH; break; 475 | case 's': *dir=DIR_SOUTH; break; 476 | case 'w': *dir=DIR_WEST; break; 477 | case 'e': *dir=DIR_EAST; break; 478 | default : assert(0); 479 | } 480 | break; 481 | } 482 | case MCPARG_NOT_FOUND: 483 | case MCPARG_NOT_PARSED: 484 | return fi; 485 | } 486 | 487 | return 0; 488 | } 489 | 490 | const char *argfmt_dir = "dir="; 491 | 492 | //////////////////// 493 | 494 | int argf_count(arg_defaults *ad, char **words, char **names, int *count) { 495 | // default name list 496 | if (!names) names = WORDLIST("count","cnt","c"); 497 | 498 | // possible option formats 499 | char ** fmt_count = WORDLIST("%d"); 500 | 501 | int fi = argparse(words, names, fmt_count, count); 502 | switch(fi) { 503 | case 0: 504 | break; 505 | case MCPARG_NOT_FOUND: 506 | case MCPARG_NOT_PARSED: 507 | return fi; 508 | default: 509 | assert(0); 510 | } 511 | //printf("Matched format >%s<, count=%d\n", fmt_count[fi], *count); 512 | 513 | return 0; 514 | } 515 | 516 | const char *argfmt_count = "count="; 517 | 518 | //////////////////// 519 | 520 | int argf_page(arg_defaults *ad, char **words, char **names, int *page) { 521 | // default name list 522 | if (!names) names = WORDLIST("page","pg","p"); 523 | 524 | // possible option formats 525 | char ** fmt_page = WORDLIST("%d"); 526 | 527 | int fi = argparse(words, names, fmt_page, page); 528 | switch(fi) { 529 | case 0: 530 | break; 531 | case MCPARG_NOT_FOUND: 532 | case MCPARG_NOT_PARSED: 533 | return fi; 534 | default: 535 | assert(0); 536 | } 537 | 538 | return 0; 539 | } 540 | 541 | const char *argfmt_page = "page="; 542 | -------------------------------------------------------------------------------- /mcp_arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "mcp_packet.h" 14 | 15 | // error codes 16 | #define MCPARG_NOT_FOUND (-1) // option not found 17 | #define MCPARG_NOT_PARSED (-2) // option found but has wrong format 18 | #define MCPARG_LOOKUP_FAILED (-3) // option found and has correct format, but the 19 | // specified ID (e.g. material name) is incorrect 20 | 21 | #define MCPARG_MAXNAMES 256 22 | #define MCPARG_MAXNLEN 256 23 | #define MCPARG_MAXFORMS 256 24 | 25 | typedef struct { 26 | char names[MCPARG_MAXNAMES][MCPARG_MAXNLEN]; 27 | // NULL-terminated list of possible names of the option 28 | 29 | int unidx; 30 | // unnamed index (-1 if no unnamed form allowed) 31 | 32 | const char *forms[MCPARG_MAXFORMS]; 33 | // possible scanf-format strings suitable for the value parsing 34 | } mcpopt; 35 | 36 | int mcparg_parse(char **words, mcpopt *opt, ...); 37 | int mcparg_find(char **words, ...); 38 | int argparse(char **words, char **names, char **fmt, ...); 39 | 40 | //////////////////////////////////////////////////////////////////////////////// 41 | 42 | int mcparg_parse_material(char **words, int argpos, char *reply, bid_t *mat, const char *suffix); 43 | int mcparg_parse_offset(char **words, int argpos, char *reply, boff_t *off); 44 | int mcparg_parse_direction(char **words, int argpos, char *reply, int *dir); 45 | int mcparg_parse_size(char **words, int argpos, char *reply, int *sx, int *sz, int *sy); 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | 49 | // macro to produce a NULL-terminated char ** - compatible list of words 50 | #define WORDLIST(...) (char*[]) { __VA_ARGS__, NULL } 51 | 52 | /* 53 | The ARG_ macros requre a correctly set scope and certain variables: 54 | - arg_defaults argdefaults : must be initialized with values 55 | - char ** words : contains the tokenized commandline 56 | - var : must be defeined and of correct type required by the argf_XXX functions 57 | - int ARG_NOTFOUND : must be defined in scope before calling ARG() 58 | - char *reply must be defined 59 | */ 60 | 61 | // parse next option 62 | #define ARG(func,names,var) \ 63 | switch(argf_##func(&ad, words, names, &var)) { \ 64 | case MCPARG_NOT_FOUND: \ 65 | ARG_NOTFOUND = 1; \ 66 | break; \ 67 | case MCPARG_NOT_PARSED: \ 68 | sprintf(reply, "Failed to parse %s option. Correct format: %s", #func, argfmt_##func); \ 69 | break; \ 70 | case MCPARG_LOOKUP_FAILED: \ 71 | sprintf(reply, "Failed to parse %s option. Name lookup failed", #func); \ 72 | break; \ 73 | } 74 | 75 | // require that the option is found, otherwise abort 76 | #define ARGREQ(func,names,var) \ 77 | ARG(func,names,var); \ 78 | if (reply[0]) goto Error; \ 79 | if (ARG_NOTFOUND) { \ 80 | sprintf(reply, "Option %s not found", #func); \ 81 | goto Error; \ 82 | } 83 | 84 | // use a default value for an option if nothing is found 85 | #define ARGDEF(func,names,var,val) \ 86 | ARG(func,names,var); \ 87 | if (reply[0]) goto Error; \ 88 | if (ARG_NOTFOUND) { \ 89 | var = val; \ 90 | } 91 | 92 | // special case for the material parsing 93 | #define ARGMAT(names,var,val) \ 94 | ARGDEF(mat,names,var,val); \ 95 | if (var.bid==0 || var.bid==0xfff) { \ 96 | sprintf(reply, "Specify material - either explicitly or by holding a buildable block"); \ 97 | goto Error; \ 98 | } 99 | 100 | // a struct containing all relevant values from gamestate that may be needed 101 | // as default values in the argument parsing. We are passing these values through 102 | // this struct in order to isolate mcparg module from the gamestate module 103 | typedef struct { 104 | int32_t px,py,pz; // player position (in block units) 105 | int pd; // player's look direction 106 | bid_t mat; // material currently held by the player 107 | bid_t mat2; // material in the next slot than what player currently holds 108 | //TODO: current mask 109 | int32_t bpsx, bpsz, bpsy; // buildplan size 110 | } arg_defaults; 111 | 112 | //////////////////////////////////////////////////////////////////////////////// 113 | 114 | int argflag(char **words, char **names); 115 | 116 | int argf_size(arg_defaults *ad, char **words, char **names, size3_t *sz); 117 | extern const char *argfmt_size; 118 | 119 | int argf_diam(arg_defaults *ad, char **words, char **names, float *diam); 120 | extern const char *argfmt_diam; 121 | 122 | int argf_pivot(arg_defaults *ad, char **words, char **names, pivot_t *pivot); 123 | extern const char *argfmt_pivot; 124 | 125 | int argf_offset(arg_defaults *ad, char **words, char **names, off3_t *offset); 126 | extern const char *argfmt_offset; 127 | 128 | int argf_pos(arg_defaults *ad, char **words, char **names, off3_t *pos); 129 | extern const char *argfmt_pos; 130 | 131 | int argf_mat(arg_defaults *ad, char **words, char **names, bid_t *mat); 132 | extern const char *argfmt_mat; 133 | 134 | int argf_dir(arg_defaults *ad, char **words, char **names, int *dir); 135 | extern const char *argfmt_dir; 136 | 137 | int argf_count(arg_defaults *ad, char **words, char **names, int *count); 138 | const char *argfmt_count; 139 | 140 | int argf_page(arg_defaults *ad, char **words, char **names, int *page); 141 | const char *argfmt_page; 142 | -------------------------------------------------------------------------------- /mcp_bplan.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include "mcp_packet.h" 18 | 19 | // this structure defines a relative block placement in a buildplan 20 | typedef struct { 21 | int32_t x,y,z; // coordinates of the block to place (relative to pivot) 22 | bid_t b; // block type, including the meta 23 | // positional meta is north-oriented 24 | } blkr; 25 | 26 | typedef struct { 27 | lh_arr_declare(blkr,plan); // currently loaded/created buildplan 28 | 29 | int32_t maxx,maxy,maxz; // max buildplan coordinate in each dimension 30 | int32_t minx,miny,minz; // min buildplan coordinate in each dimension 31 | int32_t sx,sy,sz; // buildplan size in each dimension 32 | } bplan; 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | // Management 36 | 37 | // free a buildplan object 38 | void bplan_free(bplan * bp); 39 | 40 | // recalculate buildplan extents - called when the buildplan was modified 41 | void bplan_update(bplan * bp); 42 | 43 | // dump the contents of the buildplan 44 | void bplan_dump(bplan *bp); 45 | 46 | // add a block to the buildplan 47 | int bplan_add(bplan *bp, blkr block); 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | // Helpers 51 | 52 | blkr abs2rel(pivot_t pv, blkr b); 53 | blkr rel2abs(pivot_t pv, blkr b); 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | // Parametric builds 57 | 58 | bplan * bplan_floor(int32_t wd, int32_t dp, bid_t mat); 59 | bplan * bplan_wall(int32_t wd, int32_t hg, bid_t mat); 60 | bplan * bplan_disk(float diam, bid_t mat, int edge); 61 | bplan * bplan_ball(float diam, bid_t mat, int edge); 62 | bplan * bplan_scaffolding(int wd, int hg, bid_t mat, int ladder); 63 | bplan * bplan_stairs(int32_t wd, int32_t hg, bid_t mat, int base); 64 | bplan * bplan_seal(); 65 | 66 | //////////////////////////////////////////////////////////////////////////////// 67 | // Buildplan manipulations 68 | 69 | #define TRIM_UNK -1 70 | #define TRIM_END 0 71 | 72 | #define TRIM_XE 1 73 | #define TRIM_XL 2 74 | #define TRIM_XG 3 75 | 76 | #define TRIM_YE 4 77 | #define TRIM_YL 5 78 | #define TRIM_YG 6 79 | 80 | #define TRIM_ZE 7 81 | #define TRIM_ZL 8 82 | #define TRIM_ZG 9 83 | 84 | int bplan_hollow(bplan *bp, int flat, int opaque); 85 | void bplan_extend(bplan *bp, int ox, int oz, int oy, int count); 86 | int bplan_replace(bplan *bp, bid_t mat1, bid_t mat2, int anymeta); 87 | int bplan_trim(bplan *bp, int type, int32_t value); 88 | void bplan_flip(bplan *bp, char mode); 89 | void bplan_tilt(bplan *bp, char mode); 90 | void bplan_normalize(bplan *bp); 91 | void bplan_shrink(bplan *bp); 92 | void bplan_scale(bplan *bp, int scale); 93 | 94 | //////////////////////////////////////////////////////////////////////////////// 95 | // Save/load/import 96 | 97 | int bplan_save(bplan *bp, const char *name); 98 | bplan * bplan_load(const char *name); 99 | int bplan_ssave(bplan *bp, const char *name); 100 | bplan * bplan_sload(const char *name); 101 | int bplan_csvsave(bplan *bp, const char *name); 102 | bplan * bplan_csvload(const char *name); 103 | bplan * bplan_pngload(const char *name, const char *setname); 104 | -------------------------------------------------------------------------------- /mcp_build.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "mcp_packet.h" 14 | 15 | typedef struct { 16 | bid_t material; // base material, i.e. meta reduced to material only 17 | int total; // number of such blocks in the buildtask/plan 18 | int placed; // number of blocks already placed 19 | int available; // number of blocks in the inventory 20 | } build_info_material; 21 | 22 | typedef struct { 23 | int total; 24 | int placed; 25 | int available; 26 | int limit; // current build limit, 0 if not active 27 | lh_arr_declare(build_info_material,mat); 28 | } build_info; 29 | 30 | void build_cmd(char **words, MCPacketQueue *sq, MCPacketQueue *cq); 31 | void build_clear(MCPacketQueue *sq, MCPacketQueue *cq); 32 | void build_cancel(MCPacketQueue *sq, MCPacketQueue *cq); 33 | void build_pause(); 34 | void build_update(); 35 | void build_progress(MCPacketQueue *sq, MCPacketQueue *cq); 36 | int build_packet(MCPacket *pkt, MCPacketQueue *sq, MCPacketQueue *cq); 37 | void build_preview_transmit(MCPacketQueue *cq); 38 | 39 | void build_sload(const char *name, char *reply); 40 | void build_dump_plan(); 41 | build_info * get_build_info(int plan); 42 | void calculate_material(int plan); 43 | int prefetch_material(MCPacketQueue *sq, MCPacketQueue *cq, bid_t mat); 44 | int find_evictable_slot(); 45 | -------------------------------------------------------------------------------- /mcp_game.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | uint64_t gettimestamp(); 14 | void chat_message(const char *str, MCPacketQueue *q, const char *color, int pos); 15 | 16 | void gm_packet(MCPacket *pkt, MCPacketQueue *tq, MCPacketQueue *bq); 17 | void gm_reset(); 18 | void gm_async(MCPacketQueue *sq, MCPacketQueue *cq); 19 | 20 | void gmi_change_held(MCPacketQueue *sq, MCPacketQueue *cq, int sid, int notify_client); 21 | void gmi_swap_slots(MCPacketQueue *sq, MCPacketQueue *cq, int sa, int sb); 22 | void handle_command(char *str, MCPacketQueue *tq, MCPacketQueue *bq); 23 | -------------------------------------------------------------------------------- /mcp_gamestate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #include "mcp_packet.h" 17 | #include "mcp_ids.h" 18 | #include "mcp_arg.h" 19 | #include "mcp_types.h" 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | #define GSOP_PRUNE_CHUNKS 1 24 | #define GSOP_SEARCH_SPAWNERS 2 25 | #define GSOP_TRACK_ENTITIES 3 26 | #define GSOP_TRACK_INVENTORY 4 27 | #define GSOP_REGION_LIMIT 5 28 | #define GSOP_XMIN 6 29 | #define GSOP_ZMIN 7 30 | #define GSOP_XMAX 8 31 | #define GSOP_ZMAX 9 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // entity tracking 35 | 36 | #define ENTITY_UNKNOWN 0 37 | #define ENTITY_SELF 1 38 | #define ENTITY_PLAYER 2 39 | #define ENTITY_MOB 3 40 | #define ENTITY_OBJECT 4 41 | #define ENTITY_OTHER 5 42 | 43 | static char * ENTITY_TYPES[] = { 44 | [ENTITY_UNKNOWN] = "Unknown", 45 | [ENTITY_SELF] = "Self", 46 | [ENTITY_PLAYER] = "Player", 47 | [ENTITY_MOB] = "Mob", 48 | [ENTITY_OBJECT] = "Object", 49 | [ENTITY_OTHER] = "Other", 50 | }; 51 | 52 | typedef struct _entity { 53 | int32_t id; // EID 54 | double x,y,z; // position 55 | int type; // one of the ENTITY_ variables 56 | EntityType mtype; // mob/object type as used natively 57 | int hostile; // whether marked hostile 58 | uint64_t lasthit; // timestamp when this entity was last attacked - for limiting the attack rate 59 | char name[256]; // only valid for players 60 | metadata *mdata; // entity metadata 61 | } entity; 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | // player list 65 | 66 | typedef struct { 67 | uuid_t uuid; 68 | char *name; 69 | char *dispname; 70 | } pli; 71 | 72 | //////////////////////////////////////////////////////////////////////////////// 73 | // chunk storage 74 | 75 | typedef struct { 76 | bid_t blocks[65536]; 77 | light_t light[32768]; 78 | light_t skylight[32768]; 79 | uint8_t biome[256]; 80 | nbt_t *tent; 81 | } gschunk; 82 | 83 | // chunk coord -> offset within region (1x1 regions, 32x32 chunks, 512x512 blocks) 84 | #define CC_0(X,Z) (uint32_t)((((uint64_t)(X))&0x1f)|((((uint64_t)(Z))&0x1f)<<5)) 85 | 86 | // chunk coord -> region offset within a super-region (256x256 regions, 8kx8k chunks, 128kx128k blocks) 87 | #define CC_1(X,Z) (uint32_t)(((((uint64_t)(X))>>5)&0xff)|(((((uint64_t)(Z))>>5)&0xff)<<8)) 88 | 89 | // chunk coord -> super-region offset within world (512x512 superregions, 128kx128k regions, 4Mx4M chunks, 64Mx64M blocks) 90 | #define CC_2(X,Z) (uint32_t)(((((uint64_t)(X))>>13)&0x1ff)|(((((uint64_t)(Z))>>13)&0x1ff)<<9)) 91 | 92 | // offsets in world, super-region, region -> chunk coord 93 | #define SIGNEXT(X) ((int32_t)((X)|(((X)&0x200000)?0xffc00000:0))) 94 | #define CC_X(S,R,C) SIGNEXT( (((S)&0x1ff)<<13) | (((R)&0xff)<<5) | ((C)&0x1f) ) 95 | #define CC_Z(S,R,C) SIGNEXT( (((S)&0x3fe00)<<4) | (((R)&0xff00)>>3) | (((C)&0x3e0)>>5) ) 96 | 97 | typedef struct { 98 | gschunk *chunk[32*32]; 99 | } gsregion; 100 | 101 | typedef struct { 102 | gsregion *region[256*256]; 103 | } gssreg; 104 | 105 | typedef struct { 106 | gssreg *sreg[512*512]; 107 | } gsworld; 108 | 109 | //////////////////////////////////////////////////////////////////////////////// 110 | 111 | typedef struct _gamestate { 112 | // game 113 | int64_t time; // timestamp from the last received server tick 114 | 115 | // options 116 | struct { 117 | int prune_chunks; 118 | int search_spawners; 119 | int track_entities; 120 | int track_inventory; 121 | int region_limit; 122 | } opt; 123 | 124 | struct { 125 | uint32_t eid; 126 | uuid_t uuid; 127 | int gamemode; 128 | uint8_t abilities; 129 | 130 | double x,y,z; 131 | uint8_t onground; 132 | float yaw,pitch; 133 | 134 | int crouched; 135 | 136 | // position change tracking for functions like holeradar 137 | int32_t lx,ly,lz,lo; 138 | int pos_change; 139 | 140 | float health; 141 | int32_t food; 142 | float saturation; 143 | } own; 144 | 145 | struct { 146 | uint8_t held; 147 | slot_t slots[64]; 148 | slot_t drag; 149 | 150 | // set when the inventory tracking fails to keep the inventory consistent 151 | int inconsistent; 152 | 153 | int16_t pslots[256]; 154 | int pcount; 155 | int ptype; 156 | 157 | int windowopen; // nonzero if the client has an open window 158 | int wid; // ID of the currently opened window 159 | int woffset; // main inventory starts in the window from this offset 160 | char wtype[256]; // type of window opened by the last SP_OpenWindow 161 | pos_t wpos; // last coordinates from CP_PlayerBlockPlacement - possibly a container being opened 162 | } inv; 163 | 164 | // tracked entities 165 | lh_arr_declare(entity, entity); 166 | 167 | lh_arr_declare(pli, players); 168 | 169 | gsworld overworld; 170 | gsworld end; 171 | gsworld nether; 172 | gsworld *world; 173 | 174 | int xmin,zmin,xmax,zmax; 175 | } gamestate; 176 | 177 | extern gamestate gs; 178 | 179 | //////////////////////////////////////////////////////////////////////////////// 180 | 181 | void gs_reset(); 182 | void gs_destroy(); 183 | int gs_setopt(int optid, int value); 184 | int gs_getopt(int optid); 185 | 186 | void gs_packet(MCPacket *pkt); 187 | 188 | void dump_entities(); 189 | void dump_inventory(); 190 | 191 | gschunk * find_chunk(gsworld *w, int32_t X, int32_t Z, int allocate); 192 | cuboid_t export_cuboid_extent(extent_t ex); 193 | bid_t get_block_at(int32_t x, int32_t z, int32_t y); 194 | int get_stored_area(gsworld *w, int32_t *Xmin, int32_t *Xmax, int32_t *Zmin, int32_t *Zmax); 195 | 196 | void update_chunk_containers(gschunk *gc, int X, int Z); 197 | 198 | int player_direction(); 199 | int sameitem(slot_t *a, slot_t *b); 200 | -------------------------------------------------------------------------------- /mcp_ids.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include "mcp_types.h" 16 | #include "slot.h" 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | //// Protocol IDs 20 | 21 | #define PROTO_NONE 0x00000000 22 | #define PROTO_1_8_1 0x00010801 23 | #define PROTO_1_9 0x00010900 24 | #define PROTO_1_9_2 0x00010902 25 | #define PROTO_1_9_4 0x00010904 26 | #define PROTO_1_10 0x00011000 27 | #define PROTO_1_11 0x00011100 28 | #define PROTO_1_11_2 0x00011102 29 | #define PROTO_1_12 0x00011200 30 | #define PROTO_1_12_1 0x00011201 31 | #define PROTO_1_12_2 0x00011202 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | //// Protocol Messages 35 | 36 | #define STATE_IDLE 0 37 | #define STATE_STATUS 1 38 | #define STATE_LOGIN 2 39 | #define STATE_PLAY 3 40 | 41 | #define SI(x) ((0x00<<24)|(0x##x)) 42 | #define CI(x) ((0x10<<24)|(0x##x)) 43 | #define SS(x) ((0x01<<24)|(0x##x)) 44 | #define CS(x) ((0x11<<24)|(0x##x)) 45 | #define SL(x) ((0x02<<24)|(0x##x)) 46 | #define CL(x) ((0x12<<24)|(0x##x)) 47 | #define SP(x) ((0x03<<24)|(0x##x)) 48 | #define CP(x) ((0x13<<24)|(0x##x)) 49 | 50 | #define PID(x) ((x)&0xffffff) 51 | #define PCLIENT(x) (((x)&0x10000000)!=0) 52 | #define PSTATE(x) (((x)>>24)&0x0f) 53 | 54 | // Handshakes 55 | 56 | #define CI_Handshake CI(00) 57 | 58 | // Status Query 59 | 60 | #define SS_Response SS(00) 61 | #define SS_PingResponse SS(01) 62 | 63 | #define CS_Request CS(00) 64 | #define CS_PingRequest CS(01) 65 | 66 | // Login Process 67 | 68 | #define SL_Disconnect SL(00) 69 | #define SL_EncryptionRequest SL(01) 70 | #define SL_LoginSuccess SL(02) 71 | #define SL_SetCompression SL(03) 72 | 73 | #define CL_LoginStart CL(00) 74 | #define CL_EncryptionResponse CL(01) 75 | 76 | // Play 77 | // Note: the IDs are arbitrary, but we keep them matching the latest protocol 78 | // version. IDs removed by an MC update are kept and moved to the bottom (0x80+) 79 | 80 | #define SP_SpawnObject SP(00) 81 | #define SP_SpawnExperienceOrb SP(01) 82 | #define SP_SpawnGlobalEntity SP(02) 83 | #define SP_SpawnMob SP(03) 84 | #define SP_SpawnPainting SP(04) 85 | #define SP_SpawnPlayer SP(05) 86 | #define SP_Animation SP(06) 87 | #define SP_Statistics SP(07) 88 | #define SP_BlockBreakAnimation SP(08) 89 | #define SP_UpdateBlockEntity SP(09) 90 | #define SP_BlockAction SP(0a) 91 | #define SP_BlockChange SP(0b) 92 | #define SP_BossBar SP(0c) 93 | #define SP_ServerDifficulty SP(0d) 94 | #define SP_TabComplete SP(0e) 95 | #define SP_ChatMessage SP(0f) 96 | #define SP_MultiBlockChange SP(10) 97 | #define SP_ConfirmTransaction SP(11) 98 | #define SP_CloseWindow SP(12) 99 | #define SP_OpenWindow SP(13) 100 | #define SP_WindowItems SP(14) 101 | #define SP_WindowProperty SP(15) 102 | #define SP_SetSlot SP(16) 103 | #define SP_SetCooldown SP(17) 104 | #define SP_PluginMessage SP(18) 105 | #define SP_NamedSoundEffect SP(19) 106 | #define SP_Disconnect SP(1a) 107 | #define SP_EntityStatus SP(1b) 108 | #define SP_Explosion SP(1c) 109 | #define SP_UnloadChunk SP(1d) 110 | #define SP_ChangeGameState SP(1e) 111 | #define SP_KeepAlive SP(1f) 112 | #define SP_ChunkData SP(20) 113 | #define SP_Effect SP(21) 114 | #define SP_Particle SP(22) 115 | #define SP_JoinGame SP(23) 116 | #define SP_Map SP(24) 117 | #define SP_Entity SP(25) 118 | #define SP_EntityRelMove SP(26) 119 | #define SP_EntityLookRelMove SP(27) 120 | #define SP_EntityLook SP(28) 121 | #define SP_VehicleMove SP(29) 122 | #define SP_OpenSignEditor SP(2a) 123 | #define SP_CraftRecipeResponse SP(2b) 124 | #define SP_PlayerAbilities SP(2c) 125 | #define SP_CombatEffect SP(2d) 126 | #define SP_PlayerListItem SP(2e) 127 | #define SP_PlayerPositionLook SP(2f) 128 | #define SP_UseBed SP(30) 129 | #define SP_UnlockRecipes SP(31) 130 | #define SP_DestroyEntities SP(32) 131 | #define SP_RemoveEntityEffect SP(33) 132 | #define SP_ResourcePackSent SP(34) 133 | #define SP_Respawn SP(35) 134 | #define SP_EntityHeadLook SP(36) 135 | #define SP_SelectAdvancementTab SP(37) 136 | #define SP_WorldBorder SP(38) 137 | #define SP_Camera SP(39) 138 | #define SP_HeldItemChange SP(3a) 139 | #define SP_DisplayScoreboard SP(3b) 140 | #define SP_EntityMetadata SP(3c) 141 | #define SP_AttachEntity SP(3d) 142 | #define SP_EntityVelocity SP(3e) 143 | #define SP_EntityEquipment SP(3f) 144 | #define SP_SetExperience SP(40) 145 | #define SP_UpdateHealth SP(41) 146 | #define SP_ScoreboardObjective SP(42) 147 | #define SP_SetPassengers SP(43) 148 | #define SP_Teams SP(44) 149 | #define SP_UpdateScore SP(45) 150 | #define SP_SpawnPosition SP(46) 151 | #define SP_TimeUpdate SP(47) 152 | #define SP_Title SP(48) 153 | #define SP_SoundEffect SP(49) 154 | #define SP_PlayerListHeader SP(4a) 155 | #define SP_CollectItem SP(4b) 156 | #define SP_EntityTeleport SP(4c) 157 | #define SP_Advancements SP(4d) 158 | #define SP_EntityProperties SP(4e) 159 | #define SP_EntityEffect SP(4f) 160 | #define SP_UpdateSign SP(80) // removed since 1.9.4 161 | #define SP___ SP(ff) 162 | 163 | #define CP_TeleportConfirm CP(00) 164 | #define CP_TabComplete CP(01) 165 | #define CP_ChatMessage CP(02) 166 | #define CP_ClientStatus CP(03) 167 | #define CP_ClientSettings CP(04) 168 | #define CP_ConfirmTransaction CP(05) 169 | #define CP_EnchantItem CP(06) 170 | #define CP_ClickWindow CP(07) 171 | #define CP_CloseWindow CP(08) 172 | #define CP_PluginMessage CP(09) 173 | #define CP_UseEntity CP(0a) 174 | #define CP_KeepAlive CP(0b) 175 | #define CP_Player CP(0c) 176 | #define CP_PlayerPosition CP(0d) 177 | #define CP_PlayerPositionLook CP(0e) 178 | #define CP_PlayerLook CP(0f) 179 | #define CP_VehicleMove CP(10) 180 | #define CP_SteerBoat CP(11) 181 | #define CP_CraftRecipeRequest CP(12) 182 | #define CP_PlayerAbilities CP(13) 183 | #define CP_PlayerDigging CP(14) 184 | #define CP_EntityAction CP(15) 185 | #define CP_SteerVehicle CP(16) 186 | #define CP_CraftingBookData CP(17) 187 | #define CP_ResourcePackStatus CP(18) 188 | #define CP_AdvancementTab CP(19) 189 | #define CP_HeldItemChange CP(1a) 190 | #define CP_CreativeInventoryAct CP(1b) 191 | #define CP_UpdateSign CP(1c) 192 | #define CP_Animation CP(1d) 193 | #define CP_Spectate CP(1e) 194 | #define CP_PlayerBlockPlacement CP(1f) 195 | #define CP_UseItem CP(20) 196 | #define CP_PrepareCraftingGrid CP(80) // removed since 1.12.1 197 | #define CP___ CP(ff) 198 | 199 | #define MAXPACKETTYPES 0x100 200 | 201 | //////////////////////////////////////////////////////////////////////////////// 202 | // Block type flags 203 | 204 | // bitmasks of the meta bits used as a state (i.e. affected by the game 205 | // but not directly adjustable by the player) 206 | #define I_STATE_MASK 15 207 | 208 | #define I_STATE_1 1 209 | #define I_STATE_4 4 210 | #define I_STATE_8 8 211 | #define I_STATE_C 12 212 | #define I_STATE_F 15 213 | 214 | // the ID is an item, i.e. cannot be placed as a block 215 | #define I_ITEM (1<<4) 216 | 217 | // Item does not stack (or stack size=1) 218 | #define I_NSTACK (1<<5) 219 | 220 | // item stacks only by 16 (e.g. enderpearls) 221 | #define I_S16 (1<<6) 222 | 223 | // (blocks or inventory) the metadata defines the subtype like type, color or 224 | // material. The subtype names are available in mname array 225 | #define I_MTYPE (1<<7) 226 | 227 | // the block type normally has a block entity data attached to it, e.g. signs 228 | #define I_BENTITY (1<<8) 229 | 230 | // blocks that are completely opaque - i.e. fill out the entire block 231 | // and do not have transparent areas 232 | #define I_OPAQUE (1<<9) 233 | 234 | // edible (and preferred) foods 235 | #define I_FOOD (1<<10) 236 | 237 | // metadata defines the placement orientation 238 | #define I_MPOS (1<<11) 239 | 240 | // Container blocks (chests, furnaces, hoppers, etc. - dialog opens if right-clicked) 241 | #define I_CONT (1<<12) 242 | 243 | // Blocks with adjustable setting (through right-click) 244 | #define I_ADJ (1<<13) 245 | 246 | // slab-type block - I_MPOS lower/upper placement in the meta bit 3 247 | #define I_SLAB (1<<16) 248 | 249 | // stair-type block - I_MPOS straight/upside-down in the meta bit 2, direction in bits 0-1 250 | #define I_STAIR (1<<17) 251 | 252 | // wood log type blocks 253 | #define I_LOG (1<<18) 254 | 255 | // torches and redstone torches 256 | #define I_TORCH (1<<19) 257 | 258 | // ladders, wall signs and wall banners 259 | #define I_ONWALL (1<<20) 260 | 261 | // double-slabs 262 | #define I_DSLAB (1<<21) 263 | 264 | // redstone switches 265 | #define I_RSRC (1<<22) 266 | 267 | // redstone devices (hoppers, dispensers, droppers, pistons) 268 | #define I_RSDEV (1<<23) 269 | 270 | // doors 271 | #define I_DOOR (1<<24) 272 | 273 | // trapdoors 274 | #define I_TDOOR (1<<25) 275 | 276 | // crops (planted on farmland) 277 | #define I_PLANT (1<<26) 278 | 279 | // oriented containers - chests and furnaces 280 | #define I_CHEST (1<<27) 281 | 282 | // fence gates 283 | #define I_GATE (1<<28) 284 | 285 | // observer 286 | #define I_OBSERVER (1<<29) 287 | 288 | // glazed terracota 289 | #define I_TERRACOTA (1<<30) 290 | 291 | // macros to determine armor type 292 | #define I_HELMET(id) ((id)==0x12a || (id)==0x12e || (id)==0x132 || (id)==0x136 || (id)==0x13a) 293 | #define I_CHESTPLATE(id) ((id)==0x12b || (id)==0x12f || (id)==0x133 || (id)==0x137 || (id)==0x13b) 294 | #define I_LEGGINGS(id) ((id)==0x12c || (id)==0x130 || (id)==0x134 || (id)==0x138 || (id)==0x13c) 295 | #define I_BOOTS(id) ((id)==0x12d || (id)==0x131 || (id)==0x135 || (id)==0x139 || (id)==0x13d) 296 | #define I_ELYTRA(id) ((id)==0x1bb) 297 | #define I_ARMOR(id) (I_HELMET(id) || I_CHESTPLATE(id) || I_LEGGINGS(id) || I_BOOTS(id) || I_ELYTRA(id)) 298 | 299 | typedef struct _item_id { 300 | const char * name; 301 | uint64_t flags; 302 | const char * mname[16]; 303 | } item_id; 304 | 305 | extern const item_id ITEMS[]; 306 | 307 | #define MAXITEMID 0x8ff 308 | 309 | #define STACKSIZE(item) \ 310 | ( ITEMS[item].flags&I_NSTACK ? 1 : ( ITEMS[item].flags&I_S16 ? 16 : 64 ) ) 311 | 312 | #define BLOCKTYPE(b,m) (bid_t){ { { .bid = b, .meta = m } } } 313 | 314 | //////////////////////////////////////////////////////////////////////////////// 315 | 316 | typedef struct { 317 | const char *name; 318 | uint32_t color; 319 | } biome_id; 320 | 321 | extern const biome_id BIOMES[]; 322 | 323 | //////////////////////////////////////////////////////////////////////////////// 324 | 325 | // block types we should exclude from scanning 326 | static inline int NOSCAN(int bid) { 327 | return ( bid==0x00 || // air 328 | bid==0x08 || bid==0x09 || // water 329 | bid==0x0a || bid==0x0b || // lava 330 | bid==0x1f || // tallgrass 331 | bid==0x22 || // piston head 332 | bid==0x24 || // piston extension 333 | bid==0x33 || // fire 334 | bid==0x3b || // wheat 335 | bid==0x4e || // snow layer 336 | bid==0x5a || // portal field 337 | //bid==0x63 || bid==0x64 || // giant mushrooms 338 | bid==0x8d || bid==0x8e // carrots, potatoes 339 | ); 340 | } 341 | 342 | // block types that are considered 'empty' for the block placement 343 | static inline int ISEMPTY(int bid) { 344 | return ( bid==0x00 || // air 345 | bid==0x08 || bid==0x09 || // water 346 | bid==0x0a || bid==0x0b || // lava 347 | bid==0x1f || // tallgrass 348 | bid==0x33 || // fire 349 | bid==0x4e // snow layer 350 | ); 351 | } 352 | 353 | //////////////////////////////////////////////////////////////////////////////// 354 | 355 | #include "mcp_types.h" 356 | 357 | const char * get_item_name(char *buf, slot_t *s); 358 | const char * get_bid_name(char *buf, bid_t b); 359 | 360 | int find_bid_name(const char *name); 361 | int find_meta_name(int bid, const char *name); 362 | 363 | bid_t get_base_material(bid_t mat); 364 | uint8_t get_state_mask(int bid); 365 | bid_t get_base_block_material(bid_t mat); 366 | 367 | bid_t rotate_meta(bid_t b, int times); 368 | int numrot(int from_dir, int to_dir); 369 | bid_t flip_meta(bid_t b, char mode); 370 | bid_t flip_meta_y(bid_t b); 371 | -------------------------------------------------------------------------------- /mcp_packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | /* 14 | mcp_packet : Minecraft protocol packet definitions and decoding/encoding 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "mcp_ids.h" 24 | #include "mcp_types.h" 25 | #include "slot.h" 26 | #include "entity.h" 27 | #include "helpers.h" 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | // Misc 31 | 32 | int decode_chat_json(const char *json, char *name, char *message); 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | // Server -> Client 36 | 37 | // 0x00 38 | typedef struct { 39 | uint32_t eid; 40 | uuid_t uuid; 41 | uint8_t objtype; 42 | double x; 43 | double y; 44 | double z; 45 | angle_t pitch; 46 | angle_t yaw; 47 | //TODO: object data 48 | } SP_SpawnObject_pkt; 49 | 50 | // 0x01 51 | typedef struct { 52 | uint32_t eid; 53 | double x; 54 | double y; 55 | double z; 56 | uint16_t count; 57 | } SP_SpawnExperienceOrb_pkt; 58 | 59 | // 0x03 60 | typedef struct { 61 | uint32_t eid; 62 | uuid_t uuid; 63 | int mobtype; 64 | double x; 65 | double y; 66 | double z; 67 | angle_t yaw; 68 | angle_t pitch; 69 | angle_t headpitch; 70 | int16_t vx; 71 | int16_t vy; 72 | int16_t vz; 73 | metadata *meta; 74 | } SP_SpawnMob_pkt; 75 | 76 | // 0x04 77 | typedef struct { 78 | uint32_t eid; 79 | uuid_t uuid; 80 | char title[32]; 81 | pos_t pos; 82 | uint8_t dir; 83 | } SP_SpawnPainting_pkt; 84 | 85 | // 0x05 86 | typedef struct { 87 | uint32_t eid; 88 | uuid_t uuid; 89 | double x; 90 | double y; 91 | double z; 92 | angle_t yaw; 93 | angle_t pitch; 94 | int16_t item; 95 | metadata *meta; 96 | } SP_SpawnPlayer_pkt; 97 | 98 | // 0x09 99 | typedef struct { 100 | pos_t loc; 101 | uint8_t action; 102 | nbt_t *nbt; 103 | } SP_UpdateBlockEntity_pkt; 104 | 105 | // 0x0a 106 | typedef struct { 107 | pos_t loc; 108 | uint8_t b1; 109 | uint8_t b2; 110 | int32_t type; 111 | } SP_BlockAction_pkt; 112 | 113 | // 0x0b 114 | typedef struct { 115 | pos_t pos; 116 | bid_t block; 117 | } SP_BlockChange_pkt; 118 | 119 | // 0x0f 120 | typedef struct { 121 | char *json; 122 | uint8_t pos; 123 | } SP_ChatMessage_pkt; 124 | 125 | // 0x10 126 | typedef struct { 127 | int32_t X,Z; 128 | int32_t count; 129 | blkrec *blocks; 130 | } SP_MultiBlockChange_pkt; 131 | 132 | // 0x11 133 | typedef struct { 134 | uint8_t wid; 135 | uint16_t aid; 136 | uint8_t accepted; 137 | } SP_ConfirmTransaction_pkt; 138 | 139 | // 0x12 140 | typedef struct { 141 | uint8_t wid; 142 | } SP_CloseWindow_pkt; 143 | 144 | // 0x13 145 | typedef struct { 146 | uint8_t wid; 147 | char wtype[256]; 148 | char *title; 149 | uint8_t nslots; 150 | uint32_t eid; // horse's ID - only used if the window type is a EntityHorse 151 | } SP_OpenWindow_pkt; 152 | 153 | // 0x14 154 | typedef struct { 155 | uint8_t wid; 156 | int16_t count; 157 | slot_t *slots; 158 | } SP_WindowItems_pkt; 159 | 160 | // 0x16 161 | typedef struct { 162 | uint8_t wid; 163 | int16_t sid; 164 | slot_t slot; 165 | } SP_SetSlot_pkt; 166 | 167 | // 0x1c 168 | typedef struct { 169 | float x,y,z; 170 | float radius; 171 | int32_t count; 172 | boff_t *blocks; 173 | float vx,vy,vz; 174 | } SP_Explosion_pkt; 175 | 176 | // 0x1d 177 | typedef struct { 178 | int32_t X,Z; 179 | } SP_UnloadChunk_pkt; 180 | 181 | // 0x1e 182 | typedef struct { 183 | uint8_t reason; 184 | float value; 185 | } SP_ChangeGameState_pkt; 186 | 187 | // 0x20 188 | typedef struct { 189 | int8_t cont; // ground-up continuous 190 | int8_t skylight; // whether skylight was sent; 191 | chunk_t chunk; 192 | nbt_t *te; // tile entities 193 | } SP_ChunkData_pkt; 194 | 195 | // 0x21 196 | typedef struct { 197 | uint32_t id; 198 | pos_t loc; 199 | uint32_t data; 200 | uint8_t disvol; 201 | } SP_Effect_pkt; 202 | 203 | // 0x23 204 | typedef struct { 205 | uint32_t eid; 206 | uint8_t gamemode; 207 | int32_t dimension; 208 | uint8_t difficulty; 209 | uint8_t maxplayers; 210 | char leveltype[32]; 211 | uint8_t reduced_debug_info; 212 | } SP_JoinGame_pkt; 213 | 214 | // 0x24 215 | typedef struct { 216 | uint8_t type; 217 | uint8_t x; 218 | uint8_t z; 219 | } map_icon; 220 | 221 | typedef struct { 222 | uint32_t mapid; 223 | uint8_t scale; 224 | uint8_t trackpos; 225 | uint32_t nicons; 226 | map_icon *icons; 227 | uint8_t ncols; 228 | uint8_t nrows; 229 | uint8_t X; 230 | uint8_t Z; 231 | uint32_t len; 232 | uint8_t *data; 233 | } SP_Map_pkt; 234 | 235 | // 0x26 236 | typedef struct { 237 | uint32_t eid; 238 | int16_t dx; 239 | int16_t dy; 240 | int16_t dz; 241 | uint8_t onground; 242 | } SP_EntityRelMove_pkt; 243 | 244 | // 0x27 245 | typedef struct { 246 | uint32_t eid; 247 | int16_t dx; 248 | int16_t dy; 249 | int16_t dz; 250 | angle_t yaw; 251 | angle_t pitch; 252 | uint8_t onground; 253 | } SP_EntityLookRelMove_pkt; 254 | 255 | // 0x2c 256 | typedef struct { 257 | uint8_t flags; 258 | float speed; 259 | float fov; 260 | } SP_PlayerAbilities_pkt; 261 | 262 | // 0x2e 263 | typedef struct { 264 | char pname[64]; 265 | char pval[MCP_MAXSTR]; 266 | uint8_t is_signed; 267 | char signature[MCP_MAXSTR]; 268 | } pli_prop; 269 | 270 | typedef struct { 271 | uuid_t uuid; 272 | char name[64]; 273 | lh_arr_declare(pli_prop,prop); 274 | int32_t gamemode; 275 | int32_t ping; 276 | uint8_t has_dispname; 277 | char dispname[64]; 278 | } pli_t; 279 | 280 | typedef struct { 281 | int32_t action; 282 | lh_arr_declare(pli_t,list); 283 | } SP_PlayerListItem_pkt; 284 | 285 | // 0x2f 286 | typedef struct { 287 | double x,y,z; 288 | float yaw,pitch; 289 | char flags; 290 | uint32_t tpid; 291 | } SP_PlayerPositionLook_pkt; 292 | 293 | // 0x30 294 | typedef struct { 295 | uint32_t eid; 296 | pos_t pos; 297 | } SP_UseBed_pkt; 298 | 299 | // 0x32 300 | typedef struct { 301 | uint32_t count; 302 | uint32_t *eids; 303 | } SP_DestroyEntities_pkt; 304 | 305 | // 0x35 306 | typedef struct { 307 | int32_t dimension; 308 | uint8_t difficulty; 309 | uint8_t gamemode; 310 | char leveltype[32]; 311 | } SP_Respawn_pkt; 312 | 313 | // 0x3a 314 | typedef struct { 315 | int8_t sid; 316 | } SP_HeldItemChange_pkt; 317 | 318 | // 0x3c 319 | typedef struct { 320 | uint32_t eid; 321 | metadata * meta; 322 | } SP_EntityMetadata_pkt; 323 | 324 | // 0x40 325 | typedef struct { 326 | float bar; 327 | int32_t level; 328 | int32_t exp; 329 | } SP_SetExperience_pkt; 330 | 331 | // 0x41 332 | typedef struct { 333 | float health; 334 | int32_t food; 335 | float saturation; 336 | } SP_UpdateHealth_pkt; 337 | 338 | // 0x49 339 | typedef struct { 340 | int32_t id; 341 | int32_t category; 342 | int32_t x; // multiplied by 8 343 | int32_t y; // multiplied by 8 344 | int32_t z; // multiplied by 8 345 | float vol; 346 | float pitch; 347 | } SP_SoundEffect_pkt; 348 | 349 | // 0x4c 350 | typedef struct { 351 | uint32_t eid; 352 | double x; 353 | double y; 354 | double z; 355 | angle_t yaw; 356 | angle_t pitch; 357 | uint8_t onground; 358 | } SP_EntityTeleport_pkt; 359 | 360 | // removed in 1.9.4 361 | typedef struct { 362 | pos_t pos; 363 | char line1[64]; 364 | char line2[64]; 365 | char line3[64]; 366 | char line4[64]; 367 | } SP_UpdateSign_pkt; 368 | 369 | 370 | 371 | 372 | //////////////////////////////////////////////////////////////////////////////// 373 | // Client -> Server 374 | 375 | // 0x00 376 | typedef struct { 377 | uint32_t tpid; 378 | } CP_TeleportConfirm_pkt; 379 | 380 | // 0x03 381 | typedef struct { 382 | char str[320]; 383 | } CP_ChatMessage_pkt; 384 | 385 | // 0x08 386 | typedef struct { 387 | uint8_t wid; 388 | int16_t sid; 389 | uint8_t button; 390 | uint16_t aid; 391 | uint8_t mode; 392 | slot_t slot; 393 | } CP_ClickWindow_pkt; 394 | 395 | // 0x09 396 | typedef struct { 397 | uint8_t wid; 398 | } CP_CloseWindow_pkt; 399 | 400 | // 0x0b 401 | typedef struct { 402 | uint32_t target; 403 | uint32_t action; 404 | float x,y,z; 405 | uint32_t hand; 406 | } CP_UseEntity_pkt; 407 | 408 | // 0x0d 409 | typedef struct { 410 | uint8_t onground; 411 | } CP_Player_pkt; 412 | 413 | // 0x0e 414 | typedef struct { 415 | double x; 416 | double y; 417 | double z; 418 | uint8_t onground; 419 | } CP_PlayerPosition_pkt; 420 | 421 | // 0x0f 422 | typedef struct { 423 | double x; 424 | double y; 425 | double z; 426 | float yaw; 427 | float pitch; 428 | uint8_t onground; 429 | } CP_PlayerPositionLook_pkt; 430 | 431 | // 0x10 432 | typedef struct { 433 | float yaw; 434 | float pitch; 435 | uint8_t onground; 436 | } CP_PlayerLook_pkt; 437 | 438 | // 0x14 439 | typedef struct { 440 | uint32_t status; 441 | pos_t loc; 442 | uint8_t face; 443 | } CP_PlayerDigging_pkt; 444 | 445 | // 0x15 446 | typedef struct { 447 | uint32_t eid; 448 | uint32_t action; 449 | uint32_t jumpboost; 450 | } CP_EntityAction_pkt; 451 | 452 | // 0x1a 453 | typedef struct { 454 | int16_t sid; 455 | } CP_HeldItemChange_pkt; 456 | 457 | // 0x1d 458 | typedef struct { 459 | uint32_t hand; 460 | } CP_Animation_pkt; 461 | 462 | // 0x1f 463 | typedef struct { 464 | pos_t bpos; 465 | int32_t face; 466 | int32_t hand; 467 | float cx,cy,cz; 468 | } CP_PlayerBlockPlacement_pkt; 469 | 470 | // 0x20 471 | typedef struct { 472 | int32_t hand; 473 | } CP_UseItem_pkt; 474 | 475 | 476 | 477 | //////////////////////////////////////////////////////////////////////////////// 478 | // Packet parsing for the login routines in mcproxy 479 | 480 | // CI_Handshake ( 0/0 ) 481 | typedef struct { 482 | int32_t protocolVer; 483 | char serverAddr[1024]; 484 | uint16_t serverPort; 485 | int32_t nextState; 486 | } CI_Handshake_pkt; 487 | 488 | // SL_Disconnect ( 2/0 ) 489 | typedef struct { 490 | char reason[4096]; 491 | } SL_Disconnect_pkt; 492 | 493 | // SL_EncryptionRequest ( 2/1 ) 494 | typedef struct { 495 | char serverID[4096]; 496 | uint32_t klen; 497 | uint8_t pkey[1024]; 498 | uint32_t tlen; 499 | uint8_t token[1024]; 500 | } SL_EncryptionRequest_pkt; 501 | 502 | // SL_LoginSuccess ( 2/2 ) 503 | typedef struct { 504 | char uuid[64]; 505 | char username[64]; 506 | } SL_LoginSuccess_pkt; 507 | 508 | // CL_LoginStart ( 2/0 ) 509 | typedef struct { 510 | char username[64]; 511 | } CL_LoginStart_pkt; 512 | 513 | // CL_EncryptionResponse ( 2/1 ) 514 | typedef struct { 515 | uint32_t sklen; 516 | uint8_t skey[1024]; 517 | uint32_t tklen; 518 | uint8_t token[1024]; 519 | } CL_EncryptionResponse_pkt; 520 | 521 | void decode_handshake(CI_Handshake_pkt *tpkt, uint8_t *p); 522 | uint8_t * encode_handshake(CI_Handshake_pkt *tpkt, uint8_t *w); 523 | uint8_t * encode_loginstart(CL_LoginStart_pkt *tpkt, uint8_t *w); 524 | uint8_t * encode_disconnect(SL_Disconnect_pkt *tpkt, uint8_t *w); 525 | void decode_encryption_request(SL_EncryptionRequest_pkt *tpkt, uint8_t *p); 526 | void decode_encryption_response(CL_EncryptionResponse_pkt *tpkt, uint8_t *p); 527 | 528 | //////////////////////////////////////////////////////////////////////////////// 529 | 530 | #define PKT(name) name##_pkt _##name 531 | 532 | typedef struct { 533 | union { 534 | int32_t pid; // for use as Cx_ Sx_ constants defined in mcp_ids.h 535 | struct { 536 | unsigned int type : 24; // raw packet type from the protocol 537 | unsigned int mode : 2; // protocol mode: 0:Idle, 1:Status, 2:Login, 3:Play 538 | unsigned int _mode : 2; 539 | unsigned int cl : 1; // whether this packet comes from client 540 | unsigned int _cl : 3; 541 | }; 542 | }; 543 | int32_t rawtype; // on-wire protocol type 544 | 545 | int32_t ver; // decoder will mark this with its version, so in 546 | // difficult cases you can differentiate between 547 | // changed interpretation 548 | int modified; // flag to indicate this packet was modified or is new 549 | 550 | uint8_t * raw; // raw packet data 551 | ssize_t rawlen; 552 | 553 | struct timeval ts; // timestamp when the packet was recevied 554 | 555 | // various packet types depending on pid 556 | union { 557 | PKT(SP_SpawnObject); // 00 558 | PKT(SP_SpawnExperienceOrb); // 01 559 | PKT(SP_SpawnMob); // 03 560 | PKT(SP_SpawnPainting); // 04 561 | PKT(SP_SpawnPlayer); // 05 562 | PKT(SP_UpdateBlockEntity); // 09 563 | PKT(SP_BlockAction); // 0a 564 | PKT(SP_BlockChange); // 0b 565 | PKT(SP_ChatMessage); // 0f 566 | PKT(SP_MultiBlockChange); // 10 567 | PKT(SP_ConfirmTransaction); // 11 568 | PKT(SP_CloseWindow); // 12 569 | PKT(SP_OpenWindow); // 13 570 | PKT(SP_WindowItems); // 14 571 | PKT(SP_SetSlot); // 16 572 | PKT(SP_Explosion); // 1c 573 | PKT(SP_UnloadChunk); // 1d 574 | PKT(SP_ChangeGameState); // 1e 575 | PKT(SP_ChunkData); // 20 576 | PKT(SP_Effect); // 21 577 | PKT(SP_JoinGame); // 23 578 | PKT(SP_Map); // 24 579 | PKT(SP_EntityRelMove); // 25 580 | PKT(SP_EntityLookRelMove); // 26 581 | PKT(SP_PlayerAbilities); // 2b 582 | PKT(SP_PlayerListItem); // 2d 583 | PKT(SP_PlayerPositionLook); // 2e 584 | PKT(SP_UseBed); // 2f 585 | PKT(SP_DestroyEntities); // 30 586 | PKT(SP_Respawn); // 33 587 | PKT(SP_HeldItemChange); // 37 588 | PKT(SP_EntityMetadata); // 39 589 | PKT(SP_SetExperience); // 3d 590 | PKT(SP_UpdateHealth); // 3e 591 | PKT(SP_SoundEffect); // 46 592 | PKT(SP_EntityTeleport); // 4a 593 | 594 | PKT(SP_UpdateSign); // removed in 1.9.4 595 | 596 | PKT(CP_TeleportConfirm); // 00 597 | PKT(CP_ChatMessage); // 02 598 | PKT(CP_ClickWindow); // 07 599 | PKT(CP_CloseWindow); // 08 600 | PKT(CP_UseEntity); // 0a 601 | PKT(CP_PlayerPosition); // 0c 602 | PKT(CP_PlayerPositionLook); // 0d 603 | PKT(CP_PlayerLook); // 0e 604 | PKT(CP_Player); // 0f 605 | PKT(CP_PlayerDigging); // 13 606 | PKT(CP_EntityAction); // 14 607 | PKT(CP_HeldItemChange); // 17 608 | PKT(CP_Animation); // 1a 609 | PKT(CP_PlayerBlockPlacement);// 1c 610 | PKT(CP_UseItem); // 1d 611 | 612 | }; 613 | } MCPacket; 614 | 615 | typedef struct { 616 | lh_arr_declare(MCPacket *,queue); 617 | } MCPacketQueue; 618 | 619 | //////////////////////////////////////////////////////////////////////////////// 620 | 621 | extern int currentProtocol; 622 | int set_protocol(int protocol, char * reply); 623 | 624 | MCPacket * decode_packet(int is_client, uint8_t *p, ssize_t len); 625 | ssize_t encode_packet(MCPacket *pkt, uint8_t *buf); 626 | void dump_packet(MCPacket *pkt); 627 | void free_packet (MCPacket *pkt); 628 | void queue_packet (MCPacket *pkt, MCPacketQueue *q); 629 | void packet_queue_transmit(MCPacketQueue *q, MCPacketQueue *pq, tokenbucket *tb); 630 | 631 | #define NEWPACKET(type,name) \ 632 | lh_create_obj(MCPacket,name); \ 633 | name->pid = type; \ 634 | name->ver = currentProtocol; \ 635 | type##_pkt *t##name = &name->_##type; 636 | 637 | //////////////////////////////////////////////////////////////////////////////// 638 | 639 | -------------------------------------------------------------------------------- /mcp_types.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "mcp_types.h" 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | // Coordinates 21 | 22 | extent_t ps2extent(pivot_t pv, size3_t sz) { 23 | int32_t tx,ty,tz; 24 | 25 | // calculate the point opposite of the pivot based on specified size 26 | switch (pv.dir) { 27 | case DIR_NORTH: tx=pv.pos.x+(sz.x-1); tz=pv.pos.z-(sz.z-1); break; 28 | case DIR_SOUTH: tx=pv.pos.x-(sz.x-1); tz=pv.pos.z+(sz.z-1); break; 29 | case DIR_EAST: tx=pv.pos.x+(sz.z-1); tz=pv.pos.z+(sz.x-1); break; 30 | case DIR_WEST: tx=pv.pos.x-(sz.z-1); tz=pv.pos.z-(sz.x-1); break; 31 | default: assert(0); break; 32 | } 33 | ty = pv.pos.y+(sz.y-1); 34 | 35 | // calculate the extent 36 | extent_t ex; 37 | ex.min.x = MIN(pv.pos.x,tx); 38 | ex.min.y = MIN(pv.pos.y,ty); 39 | ex.min.z = MIN(pv.pos.z,tz); 40 | ex.max.x = MAX(pv.pos.x,tx); 41 | ex.max.y = MAX(pv.pos.y,ty); 42 | ex.max.z = MAX(pv.pos.z,tz); 43 | 44 | return ex; 45 | } 46 | 47 | void free_cuboid(cuboid_t c) { 48 | int y; 49 | for(y=0; y= 4); 86 | 87 | int i; 88 | //TODO: implement aaaaaa....bbbbbb - type of printing 89 | if (len > maxbyte) len = maxbyte; 90 | for(i=0;i 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | /* 14 | mcp_types : complex data types used in the protocol and internal data 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "helpers.h" 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // various constants and helpers 25 | 26 | #define MCP_MAXSTR 32768 27 | #define MCP_MAXPLEN (4*1024*1024) 28 | 29 | uint8_t * read_string(uint8_t *p, char *s); 30 | uint8_t * write_string(uint8_t *w, const char *s); 31 | const char * limhex(uint8_t *data, ssize_t len, ssize_t maxbyte); 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // Directions 35 | 36 | #define DIR_ANY -1 37 | #define DIR_UP 0 38 | #define DIR_DOWN 1 39 | #define DIR_SOUTH 2 40 | #define DIR_NORTH 3 41 | #define DIR_EAST 4 42 | #define DIR_WEST 5 43 | 44 | extern const char ** DIRNAME; 45 | 46 | static inline int ltodir(char c) { 47 | switch (c) { 48 | case 'n': 49 | case 'N': return DIR_NORTH; 50 | case 's': 51 | case 'S': return DIR_SOUTH; 52 | case 'w': 53 | case 'W': return DIR_WEST; 54 | case 'e': 55 | case 'E': return DIR_EAST; 56 | case 'u': 57 | case 'U': return DIR_UP; 58 | case 'd': 59 | case 'D': return DIR_DOWN; 60 | } 61 | return DIR_ANY; 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | // protocol 66 | 67 | typedef uint8_t uuid_t[16]; 68 | typedef int32_t fixp; 69 | typedef uint8_t angle_t; 70 | 71 | typedef struct { 72 | union { 73 | struct { 74 | int64_t z : 26; 75 | int64_t y : 12; 76 | int64_t x : 26; 77 | }; 78 | uint64_t p; 79 | }; 80 | } pos_t; 81 | 82 | #define POS(X,Y,Z) (pos_t){ { { .x=X, .y=Y, .z=Z } } } 83 | 84 | //////////////////////////////////////////////////////////////////////////////// 85 | // coordinates 86 | 87 | typedef struct { 88 | int32_t x,y,z; 89 | } off3_t; // block offset/coordinate 90 | 91 | typedef struct { 92 | int32_t x,y,z; 93 | } size3_t; // cuboid size in blocks 94 | 95 | typedef struct { 96 | off3_t pos; 97 | int dir; 98 | } pivot_t; // pivot position and orientation 99 | 100 | typedef struct { 101 | off3_t min; // minimum coordinates (lower NW corner) 102 | off3_t max; // maximum coordinates (upper SE corner) 103 | } extent_t; // extent/cuboid in the 3D coordinates 104 | 105 | // calculate an extent from a pivot and a size 106 | extent_t ps2extent(pivot_t pv, size3_t sz); 107 | 108 | //////////////////////////////////////////////////////////////////////////////// 109 | // Map data 110 | 111 | typedef struct { 112 | union { 113 | struct { 114 | uint16_t meta : 4; 115 | uint16_t bid : 12; 116 | }; 117 | uint16_t raw; 118 | }; 119 | } bid_t; 120 | 121 | // used for skylight and blocklight 122 | typedef struct { 123 | union { 124 | struct { 125 | uint8_t l : 4; 126 | uint8_t h : 4; 127 | }; 128 | uint8_t b; 129 | }; 130 | } light_t; 131 | 132 | // represents a single 16x16x16 cube of blocks within a chunk 133 | typedef struct { 134 | bid_t blocks[4096]; 135 | light_t skylight[2048]; 136 | light_t light[2048]; 137 | } cube_t; 138 | 139 | typedef struct { 140 | int32_t X; 141 | int32_t Z; 142 | uint32_t mask; 143 | cube_t *cubes[16]; // pointers to cubes. The pointers may be NULL meaning air 144 | uint8_t biome[256]; 145 | } chunk_t; 146 | 147 | typedef struct { 148 | union { 149 | struct { 150 | uint8_t z : 4; 151 | uint8_t x : 4; 152 | }; 153 | uint8_t pos; 154 | }; 155 | uint8_t y; 156 | bid_t bid; 157 | } blkrec; 158 | 159 | typedef struct { 160 | int32_t dx,dy,dz; 161 | } boff_t; 162 | 163 | typedef struct { 164 | bid_t * data[256]; // array of horizontal slices of size sx*sz 165 | size3_t sr; // size of the requested block 166 | size3_t sa; // actual size of the data (aligned to chunk size) 167 | int32_t boff; // block offset where the xmin-zmin corner of the requested block is located 168 | // access to a block 0,0,dy : cuboid.data[y][cuboid.boff]; 169 | } cuboid_t; 170 | 171 | void free_cuboid(cuboid_t c); 172 | 173 | -------------------------------------------------------------------------------- /nbt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "nbt.h" 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // dumping 24 | 25 | static void nbt_dump_ind(nbt_t *nbt, int indent) { 26 | char ind[4096]; 27 | memset(ind, ' ', indent); 28 | ind[indent] = 0; 29 | printf("%s",ind); 30 | 31 | int i; 32 | 33 | const char *name = nbt->name ? nbt->name : ""; 34 | 35 | switch (nbt->type) { 36 | case NBT_BYTE: 37 | printf("Byte '%s'=%d\n",name,nbt->b); 38 | break; 39 | 40 | case NBT_SHORT: 41 | printf("Short '%s'=%d\n",name,nbt->s); 42 | break; 43 | 44 | case NBT_INT: 45 | printf("Int '%s'=%d\n",name,nbt->i); 46 | break; 47 | 48 | case NBT_LONG: 49 | printf("Long '%s'=%jd\n",name,nbt->l); 50 | break; 51 | 52 | case NBT_FLOAT: 53 | printf("Float '%s'=%f\n",name,nbt->f); 54 | break; 55 | 56 | case NBT_DOUBLE: 57 | printf("Double '%s'=%f\n",name,nbt->d); 58 | break; 59 | 60 | case NBT_BYTE_ARRAY: 61 | printf("Byte Array '%s'[%zd] = { ",name,nbt->count); 62 | for(i=0; icount; i++) 63 | printf("%s%d",i?", ":"",(uint8_t)nbt->ba[i]); 64 | printf(" }\n"); 65 | break; 66 | 67 | case NBT_INT_ARRAY: 68 | printf("Int Array '%s'[%zd] = { ",name,nbt->count); 69 | for(i=0; icount; i++) 70 | printf("%s%d",i?", ":"",nbt->ia[i]); 71 | printf(" }\n"); 72 | break; 73 | 74 | case NBT_STRING: 75 | printf("String '%s'=\"%s\"\n",name,nbt->st); 76 | break; 77 | 78 | case NBT_LIST: 79 | printf("List '%s' [%zd] = [\n",name,nbt->count); 80 | for(i=0; icount; i++) 81 | nbt_dump_ind(nbt->li[i], indent+2); 82 | printf("%s]\n",ind); 83 | break; 84 | 85 | case NBT_COMPOUND: 86 | printf("Compound '%s' [%zd] = {\n",name,nbt->count); 87 | for(i=0; icount; i++) 88 | nbt_dump_ind(nbt->co[i], indent+2); 89 | printf("%s}\n",ind); 90 | break; 91 | } 92 | } 93 | 94 | // print NBT contents - entry point into recursive indentation 95 | void nbt_dump(nbt_t *nbt) { 96 | if (!nbt) return; 97 | nbt_dump_ind(nbt, 0); 98 | } 99 | 100 | //////////////////////////////////////////////////////////////////////////////// 101 | // serialization 102 | 103 | // parse the payload of a known NBT token type 104 | // we need this separation of functions to be able to parse 105 | // the "headless" tokens in the arrays 106 | static nbt_t * nbt_parse_type(uint8_t **p, uint8_t type, int named) { 107 | assert(type); 108 | int i; 109 | 110 | // allocate NBT object 111 | lh_create_obj(nbt_t,nbt); 112 | nbt->type = type; 113 | nbt->count = 1; 114 | 115 | // read object's name 116 | if (named) { 117 | int16_t slen = lh_read_short_be(*p); 118 | lh_alloc_buf(nbt->name, slen+1); 119 | memmove(nbt->name, *p, slen); 120 | *p += slen; 121 | } 122 | 123 | // read the payload 124 | switch (type) { 125 | case NBT_BYTE: 126 | nbt->b = lh_read_char(*p); 127 | break; 128 | 129 | case NBT_SHORT: 130 | nbt->s = lh_read_short_be(*p); 131 | break; 132 | 133 | case NBT_INT: 134 | nbt->i = lh_read_int_be(*p); 135 | break; 136 | 137 | case NBT_LONG: 138 | nbt->l = lh_read_long_be(*p); 139 | break; 140 | 141 | case NBT_FLOAT: 142 | nbt->f = lh_read_float_be(*p); 143 | break; 144 | 145 | case NBT_DOUBLE: 146 | nbt->d = lh_read_double_be(*p); 147 | break; 148 | 149 | case NBT_BYTE_ARRAY: 150 | nbt->count = lh_read_int_be(*p); 151 | lh_alloc_num(nbt->ba, nbt->count); 152 | memmove(nbt->ba, *p, nbt->count); 153 | *p += nbt->count; 154 | break; 155 | 156 | case NBT_INT_ARRAY: 157 | nbt->count = lh_read_int_be(*p); 158 | lh_alloc_num(nbt->ia, nbt->count); 159 | for(i=0; icount; i++) 160 | nbt->ia[i] = lh_read_int_be(*p); 161 | break; 162 | 163 | case NBT_STRING: 164 | nbt->count = (uint16_t)lh_read_short_be(*p); 165 | lh_alloc_buf(nbt->st, nbt->count+1); 166 | memmove(nbt->st, *p, nbt->count); 167 | *p += nbt->count; 168 | break; 169 | 170 | case NBT_LIST: { 171 | nbt->ltype = lh_read_char(*p); 172 | nbt->count = lh_read_int_be(*p); 173 | lh_alloc_num(nbt->li, nbt->count); 174 | 175 | for(i=0; icount; i++) 176 | nbt->li[i] = nbt_parse_type(p, nbt->ltype, 0); 177 | 178 | break; 179 | } 180 | 181 | case NBT_COMPOUND: { 182 | nbt->count = 0; 183 | uint8_t ctype; 184 | while( (ctype=lh_read_char(*p)) ) { 185 | lh_resize(nbt->co, nbt->count+1); 186 | nbt->co[nbt->count] = nbt_parse_type(p, ctype, 1); 187 | nbt->count++; 188 | } 189 | break; 190 | } 191 | } 192 | 193 | return nbt; 194 | } 195 | 196 | // parse NBT object from serialized data 197 | nbt_t * nbt_parse(uint8_t **p) { 198 | uint8_t type = lh_read_char(*p); 199 | if (type == NBT_END) return NULL; 200 | return nbt_parse_type(p, type, 1); 201 | } 202 | 203 | // serialize NBT object to a buffer 204 | //FIXME: this function assumes the output buffer has sufficient size 205 | //(typically, it will be the MAXPLEN (4MiB) buffer in mcproxy used for packet encoding) 206 | void nbt_write(uint8_t **w, nbt_t *nbt) { 207 | if (!nbt) { 208 | lh_write_char(*w, NBT_END); // write terminating token 209 | return; 210 | } 211 | 212 | // if the object is unnamed - don't write type or name 213 | if (nbt->name) { 214 | lh_write_char(*w, nbt->type); 215 | ssize_t nlen = strlen(nbt->name); 216 | lh_write_short_be(*w, nlen); 217 | memmove(*w, nbt->name, nlen); 218 | *w += nlen; 219 | } 220 | 221 | int i; 222 | switch (nbt->type) { 223 | case NBT_BYTE: 224 | lh_write_char(*w, nbt->b); 225 | break; 226 | 227 | case NBT_SHORT: 228 | lh_write_short_be(*w, nbt->s); 229 | break; 230 | 231 | case NBT_INT: 232 | lh_write_int_be(*w, nbt->i); 233 | break; 234 | 235 | case NBT_LONG: 236 | lh_write_long_be(*w, nbt->l); 237 | break; 238 | 239 | case NBT_FLOAT: 240 | lh_write_float_be(*w, nbt->f); 241 | break; 242 | 243 | case NBT_DOUBLE: 244 | lh_write_double_be(*w, nbt->d); 245 | break; 246 | 247 | case NBT_BYTE_ARRAY: 248 | lh_write_int_be(*w, nbt->count); 249 | memmove(*w, nbt->ba, nbt->count); 250 | *w += nbt->count; 251 | break; 252 | 253 | case NBT_INT_ARRAY: 254 | lh_write_int_be(*w, nbt->count); 255 | for(i=0; icount; i++) 256 | lh_write_int_be(*w, nbt->ia[i]); 257 | break; 258 | 259 | case NBT_STRING: 260 | lh_write_short_be(*w, strlen(nbt->st)); 261 | memmove(*w, nbt->st, nbt->count); 262 | *w += nbt->count; 263 | break; 264 | 265 | case NBT_LIST: { 266 | lh_write_char(*w, nbt->ltype); 267 | lh_write_int_be(*w, nbt->count); 268 | 269 | for(i=0; icount; i++) 270 | nbt_write(w, nbt->li[i]); 271 | 272 | break; 273 | } 274 | case NBT_COMPOUND: { 275 | for(i=0; icount; i++) 276 | nbt_write(w, nbt->co[i]); 277 | lh_write_char(*w, NBT_END); 278 | break; 279 | } 280 | } 281 | } 282 | 283 | //////////////////////////////////////////////////////////////////////////////// 284 | // management 285 | 286 | // properly deallocate NBT objects 287 | void nbt_free(nbt_t *nbt) { 288 | if (!nbt) return; 289 | 290 | int i; 291 | 292 | switch (nbt->type) { 293 | case NBT_BYTE_ARRAY: lh_free(nbt->ba); break; 294 | case NBT_INT_ARRAY: lh_free(nbt->ia); break; 295 | case NBT_STRING: lh_free(nbt->st); break; 296 | case NBT_LIST: 297 | case NBT_COMPOUND: 298 | for(i=0; icount; i++) 299 | nbt_free(nbt->li[i]); 300 | lh_free(nbt->li); 301 | break; 302 | } 303 | lh_free(nbt->name); 304 | lh_free(nbt); 305 | } 306 | 307 | // deep-copy clone of NBT objects 308 | nbt_t * nbt_clone(nbt_t *src) { 309 | if (!src) return NULL; 310 | 311 | // allocate NBT object 312 | lh_create_obj(nbt_t,dst); 313 | 314 | // copy static elements 315 | dst->type = src->type; 316 | dst->count = src->count; 317 | if (src->name) dst->name = strdup(src->name); 318 | 319 | int i; 320 | 321 | // copy data 322 | switch (src->type) { 323 | case NBT_BYTE: 324 | dst->b = src->b; 325 | break; 326 | 327 | case NBT_SHORT: 328 | dst->s = src->s; 329 | break; 330 | 331 | case NBT_INT: 332 | dst->i = src->i; 333 | break; 334 | 335 | case NBT_LONG: 336 | dst->l = src->l; 337 | break; 338 | 339 | case NBT_FLOAT: 340 | dst->f = src->f; 341 | break; 342 | 343 | case NBT_DOUBLE: 344 | dst->d = src->d; 345 | break; 346 | 347 | case NBT_BYTE_ARRAY: 348 | lh_alloc_num(dst->ba, dst->count); 349 | memmove(dst->ba, src->ba, dst->count); 350 | break; 351 | 352 | case NBT_INT_ARRAY: 353 | lh_alloc_num(dst->ia, dst->count); 354 | memmove(dst->ia, src->ia, dst->count*sizeof(*dst->ia)); 355 | break; 356 | 357 | case NBT_STRING: 358 | dst->st = strdup(src->st); 359 | break; 360 | 361 | case NBT_LIST: 362 | dst->ltype = src->ltype; 363 | lh_alloc_num(dst->li, dst->count); 364 | for(i=0; icount; i++) 365 | dst->li[i] = nbt_clone(src->li[i]); 366 | break; 367 | 368 | case NBT_COMPOUND: 369 | lh_alloc_num(dst->co, dst->count); 370 | for(i=0; icount; i++) 371 | dst->co[i] = nbt_clone(src->co[i]); 372 | break; 373 | } 374 | 375 | return dst; 376 | } 377 | 378 | 379 | //////////////////////////////////////////////////////////////////////////////// 380 | // element access 381 | 382 | // access an element in the compound by name 383 | nbt_t * nbt_hget(nbt_t *nbt, const char *name) { 384 | if (!nbt) return NULL; 385 | if (nbt->type != NBT_COMPOUND) return NULL; 386 | 387 | int i; 388 | for(i=0; icount; i++) { 389 | const char *elname = nbt->co[i]->name; 390 | if (elname && !strcmp(elname, name)) 391 | return nbt->co[i]; 392 | } 393 | return NULL; 394 | } 395 | 396 | // access an element in an array by index 397 | nbt_t * nbt_aget(nbt_t *nbt, int idx) { 398 | if (!nbt) return NULL; 399 | if (nbt->type != NBT_LIST || idx>=nbt->count) return NULL; 400 | return nbt->li[idx]; 401 | } 402 | 403 | //////////////////////////////////////////////////////////////////////////////// 404 | // construction 405 | 406 | // create a new NBT object of given type and name 407 | // name can be set to NULL to create an unnamed object 408 | // values are specified in the variable argument list depending on type: 409 | // all basic types : value 410 | // byte/int array : type *, count 411 | // - the data will be copied 412 | // list : count, obj0, obj1, obj2, ... 413 | // - first object defines the list type 414 | // - named objects will retain their name 415 | // compoiund : count, obj0, obj1, obj2, ... 416 | // - element names are taken from inserted objects 417 | nbt_t * nbt_new(int type, const char *name, ...) { 418 | assert(type>NBT_END && type<=NBT_INT_ARRAY); 419 | 420 | lh_create_obj(nbt_t, nbt); 421 | nbt->type = type; 422 | nbt->name = name ? strdup(name) : NULL; 423 | 424 | va_list ap; 425 | va_start(ap, name); 426 | 427 | switch (type) { 428 | case NBT_BYTE: 429 | nbt->b = va_arg(ap, int32_t); 430 | break; 431 | case NBT_SHORT: 432 | nbt->s = va_arg(ap, int32_t); 433 | break; 434 | case NBT_INT: 435 | nbt->i = va_arg(ap, int32_t); 436 | break; 437 | case NBT_LONG: 438 | nbt->l = va_arg(ap, int64_t); 439 | break; 440 | case NBT_FLOAT: 441 | nbt->f = va_arg(ap, double); 442 | break; 443 | case NBT_DOUBLE: 444 | nbt->d = va_arg(ap, double); 445 | break; 446 | case NBT_BYTE_ARRAY: { 447 | int8_t *values = va_arg(ap, int8_t *); 448 | nbt->count = va_arg(ap, int); 449 | lh_alloc_buf(nbt->ba, nbt->count); 450 | memmove(nbt->ba, values, nbt->count); 451 | break; 452 | } 453 | case NBT_STRING: 454 | nbt->st = strdup(va_arg(ap, const char *)); 455 | nbt->count = strlen(nbt->st); 456 | break; 457 | case NBT_LIST: 458 | case NBT_COMPOUND: { 459 | int count = va_arg(ap, int); 460 | int i; 461 | for(i=0; icount = va_arg(ap, int); 468 | lh_alloc_num(nbt->ia, nbt->count); 469 | memmove(nbt->ia, values, nbt->count*sizeof(*values)); 470 | break; 471 | } 472 | } 473 | 474 | va_end(ap); 475 | return nbt; 476 | } 477 | 478 | //////////////////////////////////////////////////////////////////////////////// 479 | // manipulating lists/compounds 480 | 481 | //FIXME: Q&D implementation, will insert duplicate names into compounds 482 | void nbt_add(nbt_t * nbt, nbt_t * el) { 483 | assert(nbt); 484 | assert(el); 485 | 486 | assert(nbt->type == NBT_COMPOUND || nbt->type == NBT_LIST); 487 | 488 | switch (nbt->type) { 489 | case NBT_COMPOUND: 490 | assert(el->name); 491 | break; 492 | case NBT_LIST: 493 | if (nbt->ltype == NBT_END) nbt->ltype = el->type; 494 | assert(el->type == nbt->ltype); 495 | break; 496 | } 497 | nbt_t ** nel = lh_arr_new_c(nbt->co, nbt->count, 1); 498 | *nel = el; 499 | } 500 | -------------------------------------------------------------------------------- /nbt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | /* 17 | 18 | Common tag format: 19 | 20 | size name comment 21 | 1 type tag type as specified in the table below 22 | -------------------------------------------------------------------------------- 23 | 2 strlen length of the name string These two fields are available in 24 | N name name of the tag all tags except Tag_End 25 | -------------------------------------------------------------------------------- 26 | M payload size and format depends on tag type 27 | 28 | 29 | 30 | Tags 31 | 32 | 0 TAG_End 0 This tag serves no purpose but to signify the end of an 33 | open TAG_Compound. In most libraries, this type is 34 | abstracted away and never seen. TAG_End is not named. 35 | 1 TAG_Byte 1 A single signed byte 36 | 2 TAG_Short 2 A single signed short 37 | 3 TAG_Int 4 A single signed integer 38 | 4 TAG_Long 8 A single signed long (typically long long in C/C++) 39 | 5 TAG_Float 4 A single IEEE-754 single-precision floating point number 40 | 6 TAG_Double 8 A single IEEE-754 double-precision floating point number 41 | 7 TAG_Byte_Array ... A length-prefixed array of signed bytes. The prefix is 42 | a signed integer (thus 4 bytes) 43 | 8 TAG_String ... A length-prefixed UTF-8 string. The prefix is an 44 | unsigned short (thus 2 bytes) 45 | 9 TAG_List ... A list of nameless tags, all of the same type. The list 46 | is prefixed with the Type ID of the items it contains 47 | (thus 1 byte), and the length of the list as a signed 48 | integer (a further 4 bytes). 49 | 10 TAG_Compound ... Effectively a list of a named tags 50 | 11 TAG_Int_Array ... A length-prefixed array of signed integers. The prefix 51 | is a signed integer (thus 4 bytes) and indicates the 52 | number of 4 byte integers. 53 | 54 | */ 55 | 56 | #define NBT_END 0 57 | #define NBT_BYTE 1 58 | #define NBT_SHORT 2 59 | #define NBT_INT 3 60 | #define NBT_LONG 4 61 | #define NBT_FLOAT 5 62 | #define NBT_DOUBLE 6 63 | #define NBT_BYTE_ARRAY 7 64 | #define NBT_STRING 8 65 | #define NBT_LIST 9 66 | #define NBT_COMPOUND 10 67 | #define NBT_INT_ARRAY 11 68 | 69 | //typedef struct nbt_t nbt_t; 70 | 71 | typedef struct nbt_t { 72 | int type; // type of element 73 | char *name; // element name - allocated, must be freed! 74 | int ltype; // type of list elements - only valid for lists 75 | ssize_t count; // number of elements in the parsed object: 76 | // 1 for all elemental types 77 | // number of elements in the array for ba, ia 78 | // string length for str (the allocated buffer for the string has a 1 byte more for the terminator) 79 | // number of elements in the compound or list 80 | 81 | union { 82 | int8_t b; // NBT_BYTE 83 | int16_t s; // NBT_SHORT 84 | int32_t i; // NBT_INT 85 | int64_t l; // NBT_LONG 86 | float f; // NBT_FLOAT 87 | double d; // NBT_DOUBLE 88 | 89 | // all following pointers are allocated and must be freed on destruction! 90 | int8_t *ba; // NBT_BYTE_ARRAY 91 | int32_t*ia; // NBT_INT_ARRAY 92 | 93 | char *st; // NBT_STRING 94 | 95 | struct nbt_t **li; // NBT_LIST 96 | struct nbt_t **co; // NBT_COMPOUND 97 | }; 98 | } nbt_t; 99 | 100 | nbt_t * nbt_parse(uint8_t **p); 101 | void nbt_write(uint8_t **w, nbt_t *nbt); 102 | nbt_t * nbt_clone(nbt_t *nbt); 103 | void nbt_dump(nbt_t *nbt); 104 | void nbt_free(nbt_t *nbt); 105 | nbt_t * nbt_hget(nbt_t *nbt, const char *name); 106 | nbt_t * nbt_aget(nbt_t *nbt, int idx); 107 | nbt_t * nbt_new(int type, const char *name, ...); 108 | void nbt_add(nbt_t * nbt, nbt_t * el); 109 | -------------------------------------------------------------------------------- /qholder.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define LH_DECLARE_SHORT_NAMES 1 39 | 40 | #include "lh_debug.h" 41 | #include "lh_buffers.h" 42 | #include "lh_bytes.h" 43 | #include "lh_files.h" 44 | #include "lh_net.h" 45 | #include "lh_event.h" 46 | #include "lh_compress.h" 47 | #include "lh_arr.h" 48 | 49 | #include "mcp_packet.h" 50 | 51 | // forward declaration 52 | int query_auth_server(); 53 | 54 | #define DEFAULT_REMOTE_ADDR "2b2t.org" 55 | #define DEFAULT_REMOTE_PORT 25565 56 | 57 | char *o_server = DEFAULT_REMOTE_ADDR; 58 | uint16_t o_port = DEFAULT_REMOTE_PORT; 59 | char *o_user = "Dorquemada_"; 60 | int o_interval = 20; 61 | int o_protover = 210; 62 | uint32_t o_server_ip = 0xffffffff; 63 | 64 | #define RES_QUEUE 0 65 | #define RES_NOCONNECT 1 66 | #define RES_AUTHFAIL 2 67 | #define RES_TXERROR 3 68 | #define RES_RXERROR 4 69 | #define RES_OTHERERROR 5 70 | #define RES_LOGIN 6 71 | 72 | int queue_pos = -1; 73 | int queue_size = -1; 74 | 75 | uint8_t skey[16]; 76 | uint8_t enc_iv[16]; 77 | uint8_t dec_iv[16]; 78 | AES_KEY aes; 79 | 80 | 81 | int write_packet(uint8_t *data, ssize_t len, int s) { 82 | uint8_t buf[65536]; 83 | uint8_t *w = buf; 84 | 85 | write_varint(w, len); 86 | memmove(w, data, len); 87 | w+=len; 88 | 89 | ssize_t nbytes = w-buf; 90 | ssize_t wb = write(s, buf, nbytes); 91 | if (wb != nbytes) return RES_TXERROR; 92 | 93 | return 0; 94 | } 95 | 96 | int read_packet(uint8_t *rbuf, uint32_t *plen, int s) { 97 | uint8_t buf[65536]; 98 | uint8_t *p = buf; 99 | 100 | ssize_t rb = read(s, buf, 2); 101 | if (rb != 2) return RES_TXERROR; 102 | 103 | *plen = lh_read_varint(p); 104 | ssize_t ll = p-buf; 105 | rb = read(s, buf+2, *plen+ll-2); 106 | if (rb != *plen+ll-2) return RES_TXERROR; 107 | 108 | memmove(rbuf, buf+ll, *plen); 109 | 110 | return 0; 111 | } 112 | 113 | int read_enc_packet(uint8_t *rbuf, uint32_t *plen, int s) { 114 | uint8_t buf[65536]; 115 | 116 | ssize_t rb = read(s, buf, 2); 117 | if (rb != 2) return RES_TXERROR; 118 | 119 | uint8_t ubuf[65536]; 120 | int num = 0; 121 | AES_cfb8_encrypt(buf, ubuf, 2, &aes, dec_iv, &num, AES_DECRYPT); 122 | 123 | uint8_t *p = ubuf; 124 | *plen = lh_read_varint(p); 125 | ssize_t ll = p-ubuf; 126 | rb = read(s, buf+2, *plen+ll-2); 127 | if (rb != *plen+ll-2) return RES_TXERROR; 128 | 129 | num = 0; 130 | AES_cfb8_encrypt(buf+2, ubuf+2, *plen+ll-2, &aes, dec_iv, &num, AES_DECRYPT); 131 | 132 | memmove(rbuf, ubuf+ll, *plen); 133 | 134 | return 0; 135 | } 136 | 137 | // void AES_cfb8_encrypt(*in, *out, length, *key, *ivec, *num, enc); 138 | 139 | //////////////////////////////////////////////////////////////////////////////// 140 | 141 | void print_hex(char *buf, const char *data, ssize_t len) { 142 | int i; 143 | char *w = buf; 144 | int f = 0; 145 | if (data[0] < 0) { 146 | *w++ = '-'; 147 | f = 1; 148 | } 149 | for(i=0; i%s<\n",userId); 246 | printf("userName: >%s<\n",userName); 247 | printf("accessToken: >%s<\n",accessToken); 248 | printf("serverId: >%s<\n",auth); 249 | #endif 250 | 251 | char buf[4096]; 252 | sprintf(buf,"{\"accessToken\":\"%s\",\"selectedProfile\":\"%s\",\"serverId\":\"%s\"}", 253 | accessToken, userId, auth); 254 | 255 | //printf("request to session server: %s\n",buf); 256 | 257 | // perform a request with a cURL client 258 | 259 | // init curl 260 | CURL *curl = curl_easy_init(); 261 | CURLcode res; 262 | 263 | // set header options 264 | curl_easy_setopt(curl, CURLOPT_URL, "https://sessionserver.mojang.com/session/minecraft/join"); 265 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "Java/1.6.0_27"); 266 | 267 | struct curl_slist *headerlist=NULL; 268 | headerlist = curl_slist_append(headerlist, "Content-Type: application/json; charset=utf-8"); 269 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); 270 | 271 | // set body - our JSON blob 272 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf); 273 | 274 | // make a request 275 | res = curl_easy_perform(curl); 276 | if(res != CURLE_OK) 277 | fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); 278 | 279 | curl_slist_free_all(headerlist); 280 | curl_easy_cleanup(curl); 281 | 282 | return 1; 283 | } 284 | 285 | 286 | 287 | //////////////////////////////////////////////////////////////////////////////// 288 | 289 | // Send Login/Handshake to the server 290 | int send_handshake(int s) { 291 | uint8_t buf[4096]; 292 | uint8_t *w = buf; 293 | CI_Handshake_pkt pkt; 294 | pkt.protocolVer = o_protover; 295 | sprintf(pkt.serverAddr, "%s", o_server); 296 | pkt.serverPort = o_port; 297 | pkt.nextState = 2; // login 298 | 299 | write_varint(w, PID(CI_Handshake)); 300 | w = encode_handshake(&pkt, w); 301 | 302 | return write_packet(buf, w-buf, s); 303 | } 304 | 305 | int send_loginstart(int s) { 306 | uint8_t buf[4096]; 307 | uint8_t *w = buf; 308 | CL_LoginStart_pkt pkt; 309 | sprintf(pkt.username, "%s", o_user); 310 | 311 | write_varint(w, PID(CL_LoginStart)); 312 | w = encode_loginstart(&pkt, w); 313 | 314 | return write_packet(buf, w-buf, s); 315 | } 316 | 317 | int receive_encryption_req(int s) { 318 | uint8_t buf[4096]; 319 | uint32_t len; 320 | 321 | int res = read_packet(buf, &len, s); 322 | SL_EncryptionRequest_pkt erq; 323 | uint8_t *p = buf; 324 | uint32_t pid = read_varint(p); 325 | decode_encryption_request(&erq, p); 326 | 327 | // decode server PUBKEY to an RSA struct 328 | unsigned char *pp = erq.pkey; 329 | RSA *s_rsa = NULL; 330 | d2i_RSA_PUBKEY(&s_rsa, (const unsigned char **)&pp, erq.klen); 331 | if (s_rsa == NULL) { 332 | printf("Failed to decode the server's public key\n"); 333 | return 1; 334 | } 335 | //RSA_print_fp(stdout, s_rsa, 4); 336 | 337 | // generate our random key 338 | RAND_pseudo_bytes(skey, 16); 339 | 340 | // generate encryption response 341 | uint8_t *w = buf; 342 | write_varint(w, PID(CL_EncryptionResponse)); 343 | 344 | uint8_t e_skey[1024]; 345 | int eklen = RSA_public_encrypt(sizeof(skey), skey, e_skey, s_rsa, RSA_PKCS1_PADDING); 346 | write_varint(w,eklen); 347 | memcpy(w, e_skey, eklen); 348 | w += eklen; 349 | 350 | uint8_t e_token[1024]; 351 | int etlen = RSA_public_encrypt(erq.tlen, erq.token, e_token, s_rsa, RSA_PKCS1_PADDING); 352 | write_varint(w,etlen); 353 | memcpy(w, e_token, etlen); 354 | w += etlen; 355 | 356 | AES_set_encrypt_key(skey, 128, &aes); 357 | memmove(enc_iv, skey, 16); 358 | memmove(dec_iv, skey, 16); 359 | 360 | query_auth_server(erq.serverID, erq.pkey, erq.klen); 361 | 362 | return write_packet(buf, w-buf, s); 363 | } 364 | 365 | int receive_disconnect(int s) { 366 | uint8_t buf[4096]; 367 | uint32_t len; 368 | 369 | int res = read_enc_packet(buf, &len, s); 370 | 371 | uint8_t *p = buf; 372 | uint32_t pid = read_varint(p); 373 | if (pid == PID(SL_LoginSuccess)) return RES_LOGIN; 374 | if (pid != PID(SL_Disconnect)) return RES_RXERROR; 375 | 376 | int i,j=0; 377 | char str[64]; 378 | lh_clear_obj(str); 379 | for(i=0; i=len-3) return RES_OTHERERROR; 403 | 404 | return 0; 405 | } 406 | 407 | int try_connection() { 408 | int s = lh_connect_tcp4(o_server_ip, o_port); 409 | if (s<0) return RES_NOCONNECT; 410 | 411 | if (fcntl(s, F_SETFL, 0) < 0) { 412 | close(s); 413 | LH_ERROR(-1,"Failed to reset O_NONBLOCK on client socket"); 414 | } 415 | 416 | if (send_handshake(s)) { close(s); return RES_TXERROR; } 417 | if (send_loginstart(s)) { close(s); return RES_TXERROR; } 418 | if (receive_encryption_req(s)) { close(s); return RES_RXERROR; } 419 | if (receive_disconnect(s)) { close(s); return RES_RXERROR; } 420 | 421 | close(s); 422 | 423 | return RES_QUEUE; 424 | } 425 | 426 | int main(int ac, char **av) { 427 | if (av[1]) o_server = av[1]; 428 | 429 | 430 | o_server_ip = lh_dns_addr_ipv4(o_server); 431 | if (o_server_ip == 0xffffffff) 432 | LH_ERROR(1, "Failed to resolve remote server address %s",o_server); 433 | 434 | curl_global_init(CURL_GLOBAL_DEFAULT); 435 | 436 | int attempt = 0; 437 | while (1) { 438 | attempt++; 439 | time_t t; 440 | time(&t); 441 | char ts[256]; 442 | strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S",localtime(&t)); 443 | printf("Attempt %4d , %s : ", attempt, ts); 444 | 445 | switch (try_connection()) { 446 | case RES_QUEUE: 447 | printf("queued %d / %d\n", queue_pos, queue_size); 448 | sleep(o_interval); 449 | break; 450 | case RES_NOCONNECT: 451 | printf("connection failed: %s\n", strerror(errno)); 452 | sleep(2); 453 | break; 454 | case RES_TXERROR: 455 | printf("failed to send data: %s\n", strerror(errno)); 456 | sleep(2); 457 | break; 458 | case RES_RXERROR: 459 | printf("failed to send data: %s\n", strerror(errno)); 460 | sleep(2); 461 | break; 462 | } 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /schematic/kizhi_pogost.schematic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broese/mcbuild/2ba33269c3ef15ae6239fe597d018b9306e39cc5/schematic/kizhi_pogost.schematic -------------------------------------------------------------------------------- /scripts/mcb_start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RED='\033[0;31m' 4 | NC='\033[0m' 5 | 6 | APPNAME=mcproxy 7 | APPDIR=~/mcbuild 8 | 9 | if [ "$(uname -o)" == "Cygwin" ]; then APPNAME="$APPNAME.exe" ; fi 10 | find "$APPDIR" -name "*.exe" -exec chmod +x {} \; 11 | 12 | cd "$APPDIR" 13 | 14 | git fetch 15 | if [[ "$(git status | grep 'Your branch is behind' | wc | awk '{ print $1 }')" -gt 0 ]] ; then 16 | printf "${RED}Update is available, please run mcb_update${NC}\n" 17 | touch .update 18 | else 19 | rm .update 2> /dev/null 20 | fi 21 | 22 | ./"$APPNAME" "$@" 23 | -------------------------------------------------------------------------------- /scripts/mcb_update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RED='\033[0;31m' 4 | NC='\033[0m' 5 | 6 | APPDIR=~/mcbuild 7 | LIBDIR=~/libhelper 8 | 9 | if [ "$(uname -o)" == 'Cygwin' ] ; then 10 | IGNORE_ATTR="-c core.filemode=false" 11 | fi 12 | 13 | cd "$LIBDIR" 14 | git $IGNORE_ATTR pull && make 15 | 16 | cd "$APPDIR" 17 | if [ "x$1" != 'x' ] ; then 18 | if [ "$1" == 'dev' ] ; then 19 | BRANCH="master" 20 | else 21 | BRANCH="release_$1" 22 | fi 23 | git $IGNORE_ATTR checkout "$BRANCH" || ( echo "Failed to check out branch $BRANCH" ; exit 1 ) 24 | fi 25 | 26 | git $IGNORE_ATTR pull && make clean && make && make install 27 | 28 | printf "${RED}MCBuild updated${NC}\n" 29 | -------------------------------------------------------------------------------- /slot.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2016 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | /* 12 | slot : data type representing inventory/window slots 13 | */ 14 | 15 | #include 16 | 17 | #define LH_DECLARE_SHORT_NAMES 1 18 | #include 19 | #include 20 | 21 | #include "slot.h" 22 | #include "mcp_ids.h" 23 | 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // Window slots 27 | 28 | // print contents of a slot to stdout 29 | void dump_slot(slot_t *s) { 30 | char buf[256]; 31 | printf("item=%d (%s)", s->item, get_item_name(buf,s)); 32 | if (s->item != -1) { 33 | printf(", count=%d, damage=%d", s->count, s->damage); 34 | if (s->nbt) { 35 | printf(", nbt=available:\n"); 36 | //nbt_dump(s->nbt); 37 | } 38 | } 39 | } 40 | 41 | // clear slot, free any allocated data 42 | void clear_slot(slot_t *s) { 43 | if (s->nbt) { 44 | nbt_free(s->nbt); 45 | s->nbt = NULL; 46 | } 47 | s->item = -1; 48 | s->count = 0; 49 | s->damage = 0; 50 | } 51 | 52 | // ensure that an emptied slot is in a consistent state 53 | void prune_slot(slot_t *s) { 54 | if (s->count <= 0 || s->item == -1) 55 | clear_slot(s); 56 | } 57 | 58 | // make a copy of a slot, including deep-copied NBT 59 | slot_t * clone_slot(slot_t *src, slot_t *dst) { 60 | if (!dst) 61 | lh_alloc_obj(dst); 62 | 63 | clear_slot(dst); 64 | 65 | dst->item = src->item; 66 | dst->count = src->count; 67 | dst->damage = src->damage; 68 | dst->nbt = nbt_clone(src->nbt); 69 | 70 | return dst; 71 | } 72 | 73 | // swap the contents of two slots 74 | void swap_slots(slot_t *f, slot_t *t) { 75 | slot_t temp; 76 | temp = *t; 77 | *t = *f; 78 | *f = temp; 79 | } 80 | 81 | // read slot data from MC packet format 82 | uint8_t * read_slot(uint8_t *p, slot_t *s) { 83 | s->item = lh_read_short_be(p); 84 | 85 | if (s->item != -1) { 86 | s->count = lh_read_char(p); 87 | s->damage = lh_read_short_be(p); 88 | s->nbt = nbt_parse(&p); 89 | } 90 | else { 91 | s->count = 0; 92 | s->damage= 0; 93 | s->nbt = NULL; 94 | } 95 | return p; 96 | } 97 | 98 | // write slot data to MC packet format 99 | uint8_t * write_slot(uint8_t *w, slot_t *s) { 100 | lh_write_short_be(w, s->item); 101 | if (s->item == -1) return w; 102 | 103 | lh_write_char(w, s->count); 104 | lh_write_short_be(w, s->damage); 105 | 106 | nbt_write(&w, s->nbt); 107 | 108 | return w; 109 | } 110 | 111 | 112 | -------------------------------------------------------------------------------- /slot.h: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2016 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #pragma once 12 | 13 | /* 14 | slot : data type representing inventory/window slots 15 | */ 16 | 17 | #include 18 | 19 | #include "nbt.h" 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // Slots and inventory 23 | 24 | typedef struct { 25 | int16_t item; 26 | int16_t count; // actually int8_t, but we need to have a larger capacity to deal with crafting 27 | int16_t damage; 28 | nbt_t *nbt; // auxiliary data - enchantments etc. 29 | } slot_t; 30 | 31 | // print contents of a slot to stdout 32 | void dump_slot(slot_t *s); 33 | 34 | // clear slot, free any allocated data 35 | void clear_slot(slot_t *s); 36 | 37 | // ensure that an emptied slot is in a consistent state 38 | void prune_slot(slot_t *s); 39 | 40 | // make a copy of a slot, including deep-copied NBT 41 | slot_t * clone_slot(slot_t *src, slot_t *dst); 42 | 43 | // swap the contents of two slots 44 | void swap_slots(slot_t *f, slot_t *t); 45 | 46 | // read slot data from MC packet format 47 | uint8_t * read_slot(uint8_t *p, slot_t *s); 48 | 49 | // write slot data to MC packet format 50 | uint8_t * write_slot(uint8_t *w, slot_t *s); 51 | -------------------------------------------------------------------------------- /varint.c: -------------------------------------------------------------------------------- 1 | /* 2 | Authors: 3 | Copyright 2012-2015 by Eduard Broese 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 8 | 2 of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | int main(int ac, char ** av) { 18 | if (!av[1]) exit(1); 19 | 20 | uint8_t buf[16]; 21 | ssize_t len = hex_import(av[1], buf, sizeof(buf)); 22 | 23 | //hexdump(buf,len); 24 | int32_t v = lh_parse_varint(buf); 25 | printf("%x %d\n",v,v); 26 | 27 | return 0; 28 | } 29 | --------------------------------------------------------------------------------