summary refs log tree commit diff
path: root/doc
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /doc
initial commit
Diffstat (limited to 'doc')
-rw-r--r--doc/compile-fixes.md37
-rw-r--r--doc/hosting-flask.md186
-rw-r--r--doc/hosting-webclient.md97
-rw-r--r--doc/menu-architecture.md177
-rw-r--r--doc/modules.md124
-rw-r--r--doc/options.md171
-rw-r--r--doc/overriding-defaults.md63
-rw-r--r--doc/plugin-dev.md262
-rw-r--r--doc/portability.md135
-rw-r--r--doc/readme.md12
-rw-r--r--doc/sound-credits.md42
-rw-r--r--doc/strings.md329
-rw-r--r--doc/style.md34
13 files changed, 1669 insertions, 0 deletions
diff --git a/doc/compile-fixes.md b/doc/compile-fixes.md
new file mode 100644
index 0000000..0e7593a
--- /dev/null
+++ b/doc/compile-fixes.md
@@ -0,0 +1,37 @@
+Visual studio unsupported platform toolset
+---------------------
+To fix the ```Error MSB8036 The Windows SDK version 5.1 was not found. Install the required version of Windows SDK or change the SDK version in the project property pages or by right-clicking the solution and selecting "Retarget solution"```
+* Right click **ClassiCube** project (it's under the *Solution 'ClassiCube'* in the *Solution Explorer* pane)
+* Click **Properties**
+* Make sure you are in **General** tab under **Configuration Properties**
+* You should see a dropdown named **Platform Toolset**. Click on it.
+* Change it to one of the toolsets in the list that pops up.
+* Click **OK**. You should be able to compile now
+
+![image](https://user-images.githubusercontent.com/6509348/60266950-727e4780-992c-11e9-98fb-85eb34959e93.png)
+
+Common compilation errors
+---------------------
+#### Undefined reference to 'clock_gettime'
+Add ```-lrt``` when compiling. Occurs when using glibc versions before 2.17.
+
+#### fatal error: execinfo.h: No such file or directory
+Define `CC_BACKTRACE_BUILTIN` when compiling. Usually occurs when using musl.
+
+#### Undefined reference to 'backtrace'
+Define `CC_BACKTRACE_BUILTIN` when compiling. Usually occurs when using musl.
+
+Webclient patches
+---------------------
+#### Mouse scrolling not properly prevented
+With recent chrome/firefox versions, page is still scrolled and console is spammed with\
+```"[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive."```
+
+You need to to register events as a passive handler. Look for something like:
+```
+eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture);
+```
+and change to 
+```
+eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, { useCapture: eventHandler.useCapture, passive: false });
+```
diff --git a/doc/hosting-flask.md b/doc/hosting-flask.md
new file mode 100644
index 0000000..e72c0c2
--- /dev/null
+++ b/doc/hosting-flask.md
@@ -0,0 +1,186 @@
+The page provides a complete example for how to integrate the webclient into a simple website
+
+The example website will be structured like so:
+
+```
+websrv.py
+templates/play.html
+static/classisphere.js
+static/default.zip
+static/style.css
+static/jquery.js
+```
+
+## Content
+#### websrv.py
+```Python
+from flask import Flask, render_template, request
+app = Flask(__name__)
+
[email protected]("/")
+def index():
+    return '<html><h1>Welcome!</h1>Click <a href="/play">here</a> to play</h1></html>'
+
[email protected]("/play")
[email protected]("/play/")
+def play():
+    user = request.args.get('user') or 'Singleplayer'
+    ver  = request.args.get('mppass') or ''
+    addr = request.args.get('ip')
+    port = request.args.get('port') or '25565'
+
+    if addr:
+        args = "['%s', '%s', '%s', '%s']" % (user, ver, addr, port)
+    else:
+        args = "['%s']" % user
+    return render_template('play.html', game_args=args)
+
+if __name__ == "__main__":
+    app.run()
+```
+
+#### templates/play.html
+```HTML
+{% set mobile_mode = request.user_agent.platform in ('android', 'iphone', 'ipad') %}
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width">
+    <link href="/static/style.css" rel="stylesheet">
+    <script src="/static/jquery.js"></script>
+  </head>
+  <body>
+{% if mobile_mode %}
+    <style>
+      #body {min-height: 0px;}
+      .sec {padding: 0px;}
+      .row {padding: 0px;}
+    </style>
+{% else %}
+    <div id="header">
+      <div class="row">
+        <a href="/"><h1 class="columns">Home</h1></a>
+        <a href="/play"><h1 class="columns">Play</h1></a>
+      </div>
+    </div>
+{% endif %}
+<div id="body">
+<div class="sec">
+  <div class="row">
+    <canvas id="canvas" style="display:block; box-sizing:border-box; border-width:0px; padding:0; margin:0 auto; background-color: black; width:100%; height:auto;"
+            oncontextmenu="event.preventDefault()" tabindex=-1 width="1000" height="562"></canvas>
+    <span id="logmsg" style="font-size:18px;color:#F67;"></span>
+  </div>
+</div>
+<script type='text/javascript'>
+  function resizeGameCanvas() {
+    var cc_canv = $('canvas#canvas');
+    var dpi = window.devicePixelRatio;
+    var aspect_ratio = 16/9;
+
+    var viewport_w = cc_canv.parent().width();
+    var viewport_h = viewport_w / aspect_ratio;
+
+    var canv_w = Math.round(viewport_w);
+    var canv_h = Math.round(viewport_h);
+
+    if (canv_h % 2) { canv_h = canv_h - 1; }
+    if (canv_w % 2) { canv_w = canv_w - 1; }
+
+{% if mobile_mode %}
+    var screen_h = Math.min(window.innerHeight, window.outerHeight || window.innerHeight);
+    canv_h = screen_h;
+{% endif %}
+     cc_canv[0].width  = canv_w * dpi;
+     cc_canv[0].height = canv_h * dpi;
+  }
+
+  var Module = {
+    preRun: [ resizeGameCanvas ],
+    postRun: [],
+    arguments: {{game_args|safe}},
+    print: function(text) {
+      if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+      console.log(text);
+    },
+    printErr: function(text) {
+      if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+      console.error(text);
+    },
+    canvas: (function() { return document.getElementById('canvas'); })(),
+    setStatus: function(text) {
+      console.log(text);
+      document.getElementById('logmsg').innerHTML = text;
+    },
+    totalDependencies: 0,
+    monitorRunDependencies: function(left) {
+      this.totalDependencies = Math.max(this.totalDependencies, left);
+      Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+    }
+  };
+  Module.setStatus('Downloading...');
+  window.onerror = function(msg) {
+    // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+    Module.setStatus('Exception thrown, see JavaScript console (' + msg + ')');
+    Module.setStatus = function(text) {
+      if (text) Module.printErr('[post-exception status] ' + text);
+    };
+  };
+</script>
+<script async type="text/javascript" src="/static/classisphere.js"></script>
+        </div>
+    </body>
+</html>
+```
+
+#### static/classisphere.js
+Download `cs.classicube.net/client/latest/ClassiCube.js` for this
+
+#### static/default.zip
+Download `classicube.net/static/default.zip` for this
+
+#### static/style.css
+```CSS
+body { margin: 0; }
+
+.row {
+    margin-left: auto;
+    margin-right: auto;
+    max-width: 62.5em;
+}
+
+a { text-decoration: none; }
+
+.columns { display: inline-block; }
+
+.sec {
+    background:#f1ecfa;
+    padding:10px 0 5px;
+}
+
+#header { background-color:#5870b0; }
+
+#header h1 {
+    color:#fff;
+    margin:0px 10px 0px 10px;
+    width: 200px;
+}
+```
+
+#### static/jquery.js
+Download some version of jQuery for this. Version 2.1.1 is known to work.
+
+## Notes
+
+* If you don't want the game to resize to fit different resolutions, remove the `resizeGameCanvas` code.
+
+* mobile_mode is used to deliver a minified page for mobile/tablet devices
+
+## Results
+
+After all this setup, you need to install the flask package for python.
+
+Then in command prompt/terminal enter: `python websrv.py`
+
+Then navigate to `http://127.0.0.1:5000/play` in your web browser. If all goes well you should see the web client start in singleplayer.
+
+To start in multiplayer instead, navigate to `http://127.0.0.1:5000/play?user=test&ip=127.0.0.1&port=25565`
diff --git a/doc/hosting-webclient.md b/doc/hosting-webclient.md
new file mode 100644
index 0000000..5e62be3
--- /dev/null
+++ b/doc/hosting-webclient.md
@@ -0,0 +1,97 @@
+Hosting your own version of the ClassiCube webclient is relatively straightforward
+
+Only the following 3 files are required:
+1) A web page to initialise the game .js and display the game
+2) The game .js file
+3) The default texture pack
+
+TODO: more advanced sample (authentication, custom game.js, skin server)
+
+### Example setup
+
+For example, let's assume your website is setup like this:
+* `example.com/play.html`
+* `example.com/static/classisphere.js`
+* `example.com/static/default.zip`
+
+For simplicitly,
+1) Download `cs.classicube.net/client/latest/ClassiCube.js`, then upload it to `static/classisphere.js` on the webserver
+2) Download `classicube.net/static/default.zip`, then upload it to `static/default.zip` on the webserver
+
+The play.html page is the trickiest part, because how to implement this is website-specific. (depends on how the website is styled, what webserver is used, what programming language is used to generate the html, etc)
+
+#### Changing where the game downloads the texture pack from
+
+There should be this piece of code somewhere in the .JS file: `function _interop_AsyncDownloadTexturePack(rawPath) {`
+
+A bit below that, there should be `var url = '/static/default.zip';` - change that to the desired URL.
+
+#### Embedding the game in play.html
+
+The following HTML code is required to be somewhere in the webpage:
+```HTML
+<!-- the canvas *must not* have any border or padding, or mouse coords will be wrong -->
+<canvas id="canvas" style="display:block; border:0; padding:0; background-color: black;" 
+		oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
+<span id="logmsg"></span>
+
+<script type='text/javascript'>
+  var Module = {
+    preRun: [],
+    postRun: [],
+    arguments: [ {username}, {mppass}, {server ip}, {server port} ],
+    print: function(text) {
+      if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+      console.log(text);
+    },
+    printErr: function(text) {
+      if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+      console.error(text);
+    },
+    canvas: (function() { return document.getElementById('canvas'); })(),
+    setStatus: function(text) {
+      console.log(text);
+      document.getElementById('logmsg').innerHTML = text;
+    },
+    totalDependencies: 0,
+    monitorRunDependencies: function(left) {
+      this.totalDependencies = Math.max(this.totalDependencies, left);
+      Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+    }
+  };
+  Module.setStatus('Downloading...');
+  window.onerror = function(msg) {
+    // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+    Module.setStatus('Exception thrown, see JavaScript console (' + msg + ')');
+    Module.setStatus = function(text) {
+      if (text) Module.printErr('[post-exception status] ' + text);
+    };
+  };
+</script>
+<script async type="text/javascript" src="/static/classisphere.js"></script>
+```
+**To start in singleplayer instead, just use `arguments: [ {username} ],` instead**
+
+##### Variables
+* {username} - the player's username
+* {mppass} - if server verifies names, [mppass](https://wiki.vg/Classic_Protocol#User_Authentication). Otherwise leave as `''`.
+* {server ip} - the IP address of the server to connect to
+* {server port} - the port on the server to connect on (usually `'25565'`)
+
+### Complete example
+
+The links below show how to integrate the webclient into a simple website
+* [Flask (python webserver)](hosting-flask.md)
+
+### iOS / Android support
+
+The webclient is compatible with Android / iOS devices and will show a touch based UI to these devices.
+
+However, due to the limited screen size available on such devices, you should consider serving a webpage consisting of just the `<canvas>` to these devices - no header, footer or anything else.
+
+Additionally, you will likely want to ensure zooming is disabled, viewport width is same as the device's width, and that 'add to device homescreen' is fully supported. You can accomplish that by adding these three HTML tags to the page:
+```HTML
+<meta name="apple-mobile-web-app-capable" content="yes">
+<meta name="mobile-web-app-capable" content="yes">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+```
\ No newline at end of file
diff --git a/doc/menu-architecture.md b/doc/menu-architecture.md
new file mode 100644
index 0000000..66d0e2e
--- /dev/null
+++ b/doc/menu-architecture.md
@@ -0,0 +1,177 @@
+## Introduction
+
+The 2D GUI works by showing multiple 'screens' or 'layers' on top of each other. Each 'screen' usually contains one or more widgets. (e.g. a 'ButtonWidget' for a 'Close' button)
+
+Here's an example of multiple screens:
+
+![image](https://user-images.githubusercontent.com/6509348/103143562-a11cee80-476c-11eb-9e24-a854b069704f.png)
+
+The example above consists of three screens:
+1. `PauseScreen` - Top layer which consists of button widgets. Also draws a background to darken the screens/layers underneath it.
+2. `ChatScreen` - Text widgets in the bottom left area (Only one is currently used though)
+3. `HUDScreen` - Text in the top left and hotbar at the bottom
+
+TODO: Explain event processing architecture
+
+TODO: Explain pointer IDs. maybe a separate input-architecture.md file?
+
+TODO: Explain dynamic vertex buffer
+
+## Screen interface
+
+#### `void Init(void* screen)`
+
+Where you initialise state and objects that lasts for the entire duration the screen is open
+
+E.g. Hooking into events
+
+#### `void Free(void* screen)` 
+
+Where you undo the allocating/event hooking you did in `Init`.
+
+#### `void ContextRecreated(void* screen)` 
+
+Where you allocate resources used for rendering, such as fonts, textures of widgets, and the screen's dynamic vertex buffer. 
+
+E.g. where you should call `ButtonWidget_SetConst`, `TextWidget_SetConst`, etc.
+
+#### `void ContextLost(void* screen)`
+
+Where you destroy/release all that resources that were allocated in `ContextRecreated`. 
+
+Note: You **MUST** destroy all the textures/vertex buffers that you allocated, as otherwise you will get an error later with 'recreating context failed' with the Direct3D9 backend when you next try to resize the window or go fullscreen. TODO: rewrite this note
+
+#### `void Layout(void* screen)`
+
+Where you reposition all the elements (i.e. widgets + anything else) of this screen. 
+
+Note: This is called when initially showing this screen, and whenever the window is resized
+
+#### `int HandlesInputDown(void* screen, int button)` 
+
+Called whenever a input button (keyboard/mouse/gamepad) is pressed.
+
+#### `void OnInputUp(void* screen, int button)` 
+
+Called whenever a input button (keyboard/mouse/gamepad) is released.
+
+#### `int HandlesKeyPress(void* screen, char keyChar)` 
+
+Called when a key character is entered on the keyboard. 
+
+E.g. you'd get '$' here if user pressed 4 while holding shift)
+
+#### `int HandlesTextChanged(void* screen, const cc_string* str)`
+
+Called when the text changes in the on-screen keyboard. (i.e. KeyPress but for mobile text input)
+
+#### `int HandlesMouseScroll(void* screen, float wheelDelta)`
+
+Called when the wheel is scrolled on the mouse.
+
+#### `int HandlesPointerDown(void* screen, int pointerID, int x, int y)`
+
+Called whenever the left mouse or a touch finger is pressed.
+
+#### `void OnPointerUp(void* screen, int pointerID, int x, int y)`
+
+Called whenever the left mouse or a touch finger is released.
+
+#### `int HandlesPointerMove(void* screen, int pointerID, int x, int y)`
+
+Called whenever the left mouse or a touch finger is moved.
+
+#### `void BuildMesh(void* screen)` 
+
+Where you fill out the screen's dynamic vertex buffer with the vertices of all the widgets. 
+
+Note: This gets called just before `Render()` whenever the `dirty` field is set to `true`. 
+
+Note: Pointer/Input events automatically set the `dirty` field to `true` for the next frame
+
+#### `void Update(void* screen, double delta)` 
+
+Called just before `Render()` every frame and also provides the elapsed time since last render. 
+
+Typically you'd use this to update simple stuff that depends on accumulated time. 
+(E.g. whether the flashing caret should appear or not for input widgets)
+
+#### `void Render(void* screen)` 
+
+Called every frame and is where you actually draw the widgets and other stuff on-screen.
+
+## Screen members
+
+#### `struct ScreenVTABLE* VTABLE` 
+
+Set to a `ScreenVTABLE` instance which implements all of the `Screen interface` functions
+
+#### `cc_bool grabsInput` 
+
+Whether this screen grabs all input. 
+
+Note: If any screen grabs all input, then the mouse cursor becomes visible, W/A/S/D stops causing player movement, etc.
+
+#### `cc_bool blocksWorld` 
+
+Whether this screen completely and opaquely covers the game world behind it. 
+
+E.g. loading screen and disconnect screen
+
+#### `cc_bool closable` 
+
+Whether this screen is automatically closed when pressing Escape. 
+
+Note: Usually should be true for screens that are menus (e.g. pause screen)
+
+#### `cc_bool dirty` 
+
+Whether this screen will have `BuildMesh()` called on the next frame
+
+Note: This should be set to `true` whenever any event/actions that might alter the screen's appearance occurs TODO explain when automatically called?
+
+#### `int maxVertices` 
+
+The maximum number of vertices that this screen's dynamic vertex buffer may use
+
+#### `GfxResourceID vb` 
+
+This screen's dynamic vertex buffer
+
+#### `struct Widget** widgets;` 
+
+Pointer to the array of pointers to widgets that are contained in this screen
+
+#### `int numWidgets;` 
+
+The number of widgets that are contained in this screen
+
+
+## Screen notes
+
+* When the screen is shown via `Gui_Add`, the following functions are called
+  1) Init
+  2) ContextRecreated
+  3) Layout
+  4) HandlesPointerMove
+
+* When the screen is removed via `Gui_Remove`, the following functions are called
+  1) ContextLost
+  2) Free
+
+* When the screen is refreshed via `Gui_Refresh`, the following functions are called
+  1) ContextLost
+  2) ContextRecreated
+  3) Layout
+
+Note: Whenever `default.png` (font texture) changes, `Gui_Refresh` is called for all screens. Therefore, fonts should usually be allocated/freed in `ContextRecreated/ContextLost` instead of `Init/Free` to ensure that the screen still looks correct after the texture pack changes.
+
+TODO: Move this note earlier?
+
+# Putting it altogether
+
+TODO Add example of screen
+
+Simple menu example that grabs all input
+
+More complicated example too
\ No newline at end of file
diff --git a/doc/modules.md b/doc/modules.md
new file mode 100644
index 0000000..812b909
--- /dev/null
+++ b/doc/modules.md
@@ -0,0 +1,124 @@
+The overall source code is structured where each .c represents a particular module. These modules are:
+
+TODO: explain multiple backends for some Modules
+
+## 2D modules
+|Module|Functionality|
+|--------|-------|
+|Bitmap|Represents a 2D array of pixels (and encoding/decoding to PNG)
+|Drawer2D|Contains a variety of drawing operations on bitmaps (including text and fonts)
+|PackedCol|32 bit RGBA color in a format suitable for using as color component of a vertex
+|SystemFonts|Drawing, measuring, and retrieving the list of platform/system specific fonts
+
+## Audio modules
+|Module|Functionality|
+|--------|-------|
+|Audio|Playing music and dig/place/step sounds, and abstracts a PCM audio playing API
+|Vorbis| Decodes [ogg vorbis](https://xiph.org/vorbis/) audio into PCM audio samples
+
+## Entity modules
+|Module|Functionality|
+|--------|-------|
+|Entity|Represents an in-game entity, and manages updating and rendering all entities
+|EntityComponents|Various components that can be used by entities (e.g. tilt, animation, hacks state)
+|Model|Contains the list of entity models, and provides relevant methods for entity models
+|Particle|Represents particle effects, and manages rendering and spawning particles
+
+## Game modules
+|File|Functionality|
+|--------|-------|
+|Block|Stores properties and data for blocks (e.g. collide type, draw type, sound type)
+|BlockPhysics|Implements simple block physics for singleplayer
+|Camera|Represents a camera (can be first or third person)
+|Chat|Manages sending, adding, logging and handling chat
+|Game|Manages the overall game loop, state, and variables (e.g. renders a frame, runs scheduled tasks)
+|Input|Manages keyboard, mouse, and touch state and events, and implements base handlers for them
+|Inventory|Manages inventory hotbar, and ordering of blocks in the inventory menu
+
+## Game gui modules
+|File|Functionality|
+|--------|-------|
+|Gui|Describes and manages the 2D GUI elements on screen
+|IsometricDrawer|Draws 2D isometric blocks for the hotbar and inventory UIs
+|Menus|Contains all 2D non-menu screens (e.g. inventory, HUD, loading, chat)
+|Screens|Contains all 2D menu screens (e.g. pause menu, keys list menu, font list menu)
+|Widgets|Contains individual GUI widgets (e.g. button, label)
+
+## Graphics modules
+|Module|Functionality|
+|--------|-------|
+|Builder|Converts a 16x16x16 chunk into a mesh of vertices
+|Drawer|Draws the vertices for a cuboid region
+|Graphics|Abstracts a 3D graphics rendering API
+
+## I/O modules
+|Module|Functionality|
+|--------|-------|
+|Deflate|Decodes and encodes data compressed using DEFLATE (in addition to GZIP/ZLIB headers)
+|Stream|Abstract reading and writing data to/from various sources in a streaming manner
+
+## Launcher modules 
+|Module|Functionality|
+|--------|-------|
+|Launcher|Manages the overall launcher loop, state, and variables (e.g. resets pixels in areas, marks areas as needing to be redrawn)
+|LBackend|Handles the rendering of widgets and forwarding input events to screens/menus
+|LScreens|Contains all the menus in the launcher (e.g. servers list, updates menu, main menu)
+|LWeb|Responsible for launcher related web requests (e.g. signing in, fetching servers list)
+|LWidgets|Contains individual launcher GUI widgets (e.g. button, label, input textbox)
+|Resources|Responsible for checking, downloading, and creating the default assets (e.g. default.zip, sounds)
+
+## Map modules 
+|Module|Description|
+|--------|-------|
+|Formats|Imports/exports a world from/to several map file formats (e.g. .cw, .dat, .lvl)
+|Generator|Generates a new world in either a flatgrass or Minecraft Classic style
+|Lighting|Gets lighting colors at coordinates in the world
+|World|Manages fixed size 3D array of blocks and associated environment metadata
+
+## Math/Physics modules 
+|Module|Description|
+|--------|-------|
+|ExtMath|Math functions, math constants, and a Random Number Generator
+|Physics|AABBs and geometry intersection
+|Picking|Performs raytracing to e.g. determine the picked/selected block in the world
+|Vectors|Contains vector,matrix,and frustum culling
+
+## Network modules 
+|Module|Description|
+|--------|-------|
+|Http|Performs GET and POST requests in the background
+|Protocol|Implements Minecraft Classic, CPE, and WoM environment protocols
+|Server|Manages a connection to a singleplayer or multiplayer server
+|SSL|Performs SSL/TLS encryption and decryption
+
+## Platform modules
+|Module|Description|
+|--------|-------|
+|Logger|Manages logging to client.log, and dumping state in both intentional and unhandled crashes
+|Platform|Abstracts platform specific functionality. (e.g. opening a file, allocating memory, starting a thread)
+|Program|Parses command line arguments, and then starts either the Game or Launcher
+|Window|Abstracts creating and managing a window (e.g. setting titlebar text, entering fullscreen)
+
+## Rendering modules
+|Module|Description|
+|--------|-------|
+|AxisLinesRenderer|Renders 3 lines showing direction of each axis
+|EnvRenderer|Renders environment of the world (clouds, sky, skybox, world sides/edges, etc)
+|HeldBlockRenderer|Renders the block currently being held in bottom right corner
+|MapRenderer|Renders the blocks of the world by diving it into chunks, and manages sorting/updating these chunks
+|SelOutlineRenderer|Renders an outline around the block currently being looked at
+|SelectionBox|Renders and stores selection boxes
+
+## Texture pack modules
+|Module|Functionality|
+|--------|-------|
+|Animations|Everything relating to texture animations (including default water/lava ones)
+|TexturePack|Everything relating to texture packs (e.g. extracting .zip, terrain atlas, etc)
+
+## Utility modules
+|Module|Functionality|
+|--------|-------|
+|Event|Contains all events and provies helper methods for using events
+|Options|Retrieves options from and sets options in options.txt
+|String|Implements operations for a string with a buffer, length, and capacity
+|Utils|Various general utility functions
diff --git a/doc/options.md b/doc/options.md
new file mode 100644
index 0000000..35f7ffc
--- /dev/null
+++ b/doc/options.md
@@ -0,0 +1,171 @@
+Listed below are all of the options supported in options.txt
+
+# Global options
+
+### HTTP options
+|Name|Default|Description|
+|--|--|--|
+`http-no-https`|`false`|Whether `https://` support is disabled<br>**Disabling means your account password is transmitted in plaintext**
+`https-verify`|`false`|Whether to validate 'https://' certificates returned by webservers<br>**Disabling this is a bad idea, but is still less bad than `http-no-https`**
+
+### Text drawing options
+|Name|Default|Description|
+|--|--|--|
+`gui-arialchatfont`|`false`|Whether system fonts are used instead of default.png for drawing most text
+`gui-blacktextshadows`|`false`|Whether text shadow color is pure black instead of faded
+`gui-fontname`||Name of preferred system font to use for text rendering
+
+### Window options
+|Name|Default|Description|
+|--|--|--|
+`landscape-mode`|`false`|Whether to force landscape orientation<br>**Only supported on Android/iOS**
+
+# Launcher options
+
+### General options
+|Name|Default|Description|
+|--|--|--|
+`autocloselauncher`|`false`|Whether to automatically close the launcher after the game is started
+`launcher-cc-username`||Account username
+`launcher-cc-password`||Encrypted account password
+`nostalgia-classicbg`|false`|Whether to draw classic minecraft.net like background
+
+### Web options
+|Name|Default|Description|
+|--|--|--|
+`server-services`|`https://www.classicube.net/api`|URL for account services (login and server list)
+`launcher-session`||Encrypted session cookie<br>Valid session cookies avoids MFA/2FA check
+
+### Theme colors
+|Name|Description|
+|--|--|
+`launcher-back-col`|Background/base color
+`launcher-btn-border-col`|Color of border arround buttons
+`launcher-btn-fore-active-col`|Color of button when mouse is hovered over
+`launcher-btn-fore-inactive-col`|Normal color of buttons
+`launcher-btn-highlight-inactive-col`|Color of line at top of buttons
+
+### Direct connect
+|Name|Description|
+|--|--|
+`launcher-dc-username`|Username to use when connecting
+`launcher-dc-ip`|IP address or hostname to connect to
+`launcher-dc-port`|Port to connect on
+`launcher-dc-mppass`|Encrypted mppass to use when connecting
+
+### Resume
+|Name|Description|
+|--|--|
+`launcherserver`|Name of server to connect to<br>Hint for user, not actually used for connecting
+`launcher-username`|Username to use when connecting
+`launcher-ip`|IP address or hostname to connect to
+`launcher-port`|Port to connect on
+`launcher-mppass`|Encrypted mppass to use when connecting
+
+# Game options
+
+### Audio options
+|Name|Default|Description|
+|--|--|--|
+`soundsvolume`|`0` for webclient<br>`100` elsewhere|Volume of game sounds (e.g. break/walk sounds)<br>Volume must be between 0 and 100
+`musicvolume`|`0` for webclient<br>`100` elsewhere|Volume of game background music<br>Volume must be between 0 and 100
+`music-mindelay`|`120` (2 minutes)|Minimum delay before next music track is played <br>Delay must be between 0 and 3600
+`music-maxdelay`|`420` (7 minutes)|Maximum delay before next music track is played <br>Delay must be between 0 and 3600
+
+### Block physics options
+|Name|Default|Description|
+|--|--|--|
+`singleplayerphysics`|`true`|Whether block physics are enabled in singleplayer
+
+### Chat options
+|Name|Default|Description|
+|--|--|--|
+`chat-logging`|`false` for mobile/web<br>`true` elsewhere|Whether to log chat messages to disc
+
+### HTTP options
+|Name|Default|Description|
+|--|--|--|
+`http-skinserver`|`http://classicube.s3.amazonaws.com/skin`|URL where player skins are downloaded from
+
+### Map rendering options
+|Name|Default|Description|
+|--|--|--|
+`gfx-smoothlighting`|`false`|Whether smooth/advanced lighting is enabled
+`gfx-maxchunkupdates`|`30`|Max number of chunks built in one frame<br>Must be between 4 and 1024
+
+### Camera options
+|Name|Default|Description|
+|--|--|--|
+`mousesensitivity`|`40` for Windows<br>`30` elsewhere|How sensitive camera rotation is to mouse movement<br>Sensitivity must be between 1 and 200
+`hacks-fov`|`70`|Field of view<br>Must be between 1 and 179
+`hacks-cameraclipping`|`true`|Whether third person camera is clipped to not go inside blocks
+`invertmouse`|`true`|Whether vertical mouse movement direction is inverted
+`camera-smooth`|`false`|Whether smooth camera mode is enabled
+`cameramass`|`20`|How smooth the smooth camera is<br>Value must be between 1 and 100
+
+### Game options
+|Name|Default|Description|
+|--|--|--|
+./Game.c:       Game_ClassicMode       = Options_GetBool(OPT_CLASSIC_MODE, false);
+./Game.c:       Game_ClassicHacks      = Options_GetBool(OPT_CLASSIC_HACKS, false);
+./Game.c:       Game_AllowCustomBlocks = Options_GetBool(OPT_CUSTOM_BLOCKS, true);
+./Game.c:       Game_UseCPE            = Options_GetBool(OPT_CPE, true);
+./Game.c:       Game_SimpleArmsAnim    = Options_GetBool(OPT_SIMPLE_ARMS_ANIM, false);
+./Game.c:       Game_ViewBobbing       = Options_GetBool(OPT_VIEW_BOBBING, true);
+./Game.c:       Game_ViewDistance     = Options_GetInt(OPT_VIEW_DISTANCE, 8, 4096, 512);
+./Game.c:       Game_BreakableLiquids = !Game_ClassicMode && Options_GetBool(OPT_MODIFIABLE_LIQUIDS, false);
+./Game.c:       Game_AllowServerTextures = Options_GetBool(OPT_SERVER_TEXTURES, true);
+
+### Hacks options
+|Name|Default|Description|
+|--|--|--|
+`hacks-hacksenabled`|`true`|Whether hacks are enabled at all<br>Has no effect in 'classic only' game mode
+`hacks-speedmultiplier`|`10.0`|Speed multiplier/factor when speedhacks are active<br>Multiplier must be between 0.1 and 50.0
+`hacks-pushbackplacing`|`false`|Whether to enable pushback placing mode
+`hacks-noclipslide`|`false`|Whether to still slide for a bit after a movement button is released in noclip mode
+`hacks-womstylehacks`|`false`|Whether to enable WoM client style hacks (e.g. ludicrous triple jump)
+`hacks-fullblockstep`|`false`|Whether to automatically climb up 1.0 (default is only 0.5) tall blocks
+`hacks-jumpvelocity`|`0.42`|Initial vertical velocity when you start a jump<br>Velocity must be between 0.0 and 52.0
+`hacks-perm-msgs`|`true`|Whether to show a message in chat if you attempt to use a currently disabled hack
+
+## General rendering options
+|Name|Default|Description|
+|--|--|--|
+`gfx-mipmaps`|`false`|Whether to use mipmaps to reduce faraway texture noise
+`fpslimit`|`LimitVSync`|Strategy used to limit FPS<br>Strategies: LimitVSync, Limit30FPS, Limit60FPS, Limit120FPS, Limit144FPS, LimitNone
+`normal`|`normal`|Environmental effects render mode<br>Modes: normal, normalfast, legacy, legacyfast<br>- legacy improves appearance on some older GPUs<br>- fast disables clouds, fog and overhead sky
+
+## Other rendering options
+|Name|Default|Description|
+|--|--|--|
+`nostalgia-classicarm`|Classic mode|Whether to render your own arm in classic or modern minecraft style
+`gui-blockinhand`|`true`|Whether to show block currently being held in bottom right corner
+`namesmode`|`Hovered`|Entity nametag rendering mode<br>None, Hovered, All, AllHovered, AllUnscaled
+`entityshadow`|`None`|Entity shadow rendering mode<br>None, SnapToBlock, Circle, CircleAll
+
+### Texture pack options
+|Name|Default|Description|
+|--|--|--|
+`defaulttexpack`|`default.zip`|Filename of default texture pack
+
+### Window options
+|Name|Default|Description|
+|--|--|--|
+`window-width`|`854`|Width of game window<br>Width must be between 0 and display width
+`window-height`|`480`|Height of game window<br>Height must be between 0 and display height
+`win-grab-cursor`|`false`|Whether to grab exclusive control over the cursor<br>**Only supported on Linux/BSD**
+
+./Gui.c:        Gui.Chatlines       = Options_GetInt(OPT_CHATLINES, 0, 30, Gui.DefaultLines);
+./Gui.c:        Gui.ClickableChat   = !Game_ClassicMode && Options_GetBool(OPT_CLICKABLE_CHAT,   !Input_TouchMode);
+./Gui.c:        Gui.TabAutocomplete = !Game_ClassicMode && Options_GetBool(OPT_TAB_AUTOCOMPLETE, true);
+./Gui.c:        Gui.ClassicTexture = Options_GetBool(OPT_CLASSIC_GUI, true)      || Game_ClassicMode;
+./Gui.c:        Gui.ClassicTabList = Options_GetBool(OPT_CLASSIC_TABLIST, false) || Game_ClassicMode;
+./Gui.c:        Gui.ClassicMenu    = Options_GetBool(OPT_CLASSIC_OPTIONS, false) || Game_ClassicMode;
+./Gui.c:        Gui.ClassicChat    = Options_GetBool(OPT_CLASSIC_CHAT, false)    || Game_PureClassic;
+./Gui.c:        Gui.ShowFPS        = Options_GetBool(OPT_SHOW_FPS, true);
+./Gui.c:        Gui.RawInventoryScale = Options_GetFloat(OPT_INVENTORY_SCALE, 0.25f, 5.0f, 1.0f);
+./Gui.c:        Gui.RawHotbarScale    = Options_GetFloat(OPT_HOTBAR_SCALE,    0.25f, 5.0f, 1.0f);
+./Gui.c:        Gui.RawChatScale      = Options_GetFloat(OPT_CHAT_SCALE,      0.25f, 5.0f, 1.0f);
+./Gui.c:        Gui.RawTouchScale     = Options_GetFloat(OPT_TOUCH_SCALE,     0.25f, 5.0f, 1.0f);
+./Gui.c:        Gui._onscreenButtons = Options_GetInt(OPT_TOUCH_BUTTONS, 0, Int32_MaxValue,
+./Input.c:              mapping = Options_GetEnum(name.buffer, KeyBind_Defaults[i], Input_Names, INPUT_COUNT);
diff --git a/doc/overriding-defaults.md b/doc/overriding-defaults.md
new file mode 100644
index 0000000..102de90
--- /dev/null
+++ b/doc/overriding-defaults.md
@@ -0,0 +1,63 @@
+Although ClassiCube strives to be as platform independent as possible, in some cases it will need to use system specific code
+
+For instance:
+* Texture creation for 3D graphics rendering
+* Buffer allocation for audio output
+* High resolution time measurement
+* Window creation
+
+For simplicity, related system specific code is grouped together as a Module (e.g. `Audio`), which can then be implemented using a backend (e.g. `WinMM`, `OpenAL`, `OpenSL ES`, etc)
+
+#### Note: By default, ClassiCube automatically selects the recommended backends for the system. <br> It is recommended that you do *NOT* change the backends unless you know exactly what you are doing.
+
+However, on some systems there are potentially multiple backends for a Module. For example on Windows:
+* OpenGL could be used instead of Direct3D 9 for the 3D rendering backend
+* SDL could be used instead of the native WinAPI for the window backend
+
+TODO finish this
+
+TODO introduction (explaining platform specific modules, and how classicube has to integrate with one of them)
+
+There are two ways of changing the backend that gets used for the system:
+1) Changing the defines in `Core.h`
+2) Adding `-DCC_BUILD_MANUAL` to compilation flags and then manually defining module backends via additional compilation flags
+
+When manually compiling the source code, 1) is usually the easiest. <br>
+For automated scripts compiling every single commit, 2) is the recommended approach
+TODO: Move this into table
+
+
+### 3D Graphics backends
+* CC_BUILD_D3D9 - Direct3D 9
+* CC_BUILD_D3D11 - Direct3D 11
+* CC_BUILD_GL - OpenGL
+
+The OpenGL backend can be further customised:
+* CC_BUILD_GL11 - (must also have CC_BUILD_GL defined)
+* CC_BUILD_GLMODERN - (must also have CC_BUILD_GL defined)
+* CC_BUILD_GLES (must also have CC_BUILD_GL defined)
+
+### OpenGL context backends
+* CC_BUILD_EGL
+* CC_BUILD_WGL
+
+### HTTP backends
+* CC_BUILD_CURL
+* CC_BUILD_HTTPCLIENT - custom HTTP client
+* CC_BUILD_WININET
+* CC_BUILD_CFNETWORK
+
+### SSL backends
+* CC_BUILD_SCHANNEL
+* CC_BUILD_BEARSSL
+
+### Window backends
+* CC_BUILD_SDL
+* CC_BUILD_X11
+* CC_BUILD_WINGUI
+
+### Platform backends
+* CC_BUILD_POSIX
+* CC_BUILD_WIN
+
+TODO fill in rest
\ No newline at end of file
diff --git a/doc/plugin-dev.md b/doc/plugin-dev.md
new file mode 100644
index 0000000..99ca1e4
--- /dev/null
+++ b/doc/plugin-dev.md
@@ -0,0 +1,262 @@
+This document details how to compile a basic plugin in Visual Studio, MinGW, or GCC/Clang.
+
+To find the functions and variables available for use in plugins, look for `CC_API`/`CC_VAR` in the .h files.
+
+[Source code of some actual plugins](https://github.com/ClassiCube/ClassiCube-Plugins/)
+
+### Setup
+
+You need to download and install either Visual Studio, MinGW, or GCC/Clang.
+
+*Note: MinGW/GCC/Clang are relatively small, while Visual Studio is gigabytes in size.  
+If you're just trying to compile a plugin on Windows you might want to use MinGW. See main readme.*
+
+I assume your directory is structured like this:
+```
+src/...
+TestPlugin.c
+```
+Or in other words, in a directory somewhere, you have a file named `TestPlugin.c`, and then a sub-directory named `src` which contains the game's source code.
+
+### Basic plugin
+```C
+#include "src/Chat.h"
+#include "src/Game.h"
+#include "src/String.h"
+
+static void TestPlugin_Init(void) {
+        cc_string msg = String_FromConst("Hello world!");
+        Chat_Add(&msg);
+}
+
+int Plugin_ApiVersion = 1;
+struct IGameComponent Plugin_Component = { TestPlugin_Init };
+```
+Here's the idea for a basic plugin that shows "Hello world" in chat when the game starts. Alas, this won't compile...
+
+### Basic plugin boilerplate
+```C
+#ifdef _WIN32
+    #define CC_API __declspec(dllimport)
+    #define CC_VAR __declspec(dllimport)
+    #define EXPORT __declspec(dllexport)
+#else
+    #define CC_API
+    #define CC_VAR
+    #define EXPORT __attribute__((visibility("default")))
+#endif
+
+#include "src/Chat.h"
+#include "src/Game.h"
+#include "src/String.h"
+
+static void TestPlugin_Init(void) {
+    cc_string msg = String_FromConst("Hello world!");
+    Chat_Add(&msg);
+}
+
+EXPORT int Plugin_ApiVersion = 1;
+EXPORT struct IGameComponent Plugin_Component = { TestPlugin_Init };
+```
+With this boilerplate, we're ready to compile the plugin.
+All plugins require this boilerplate, so feel free to copy and paste it.
+
+---
+
+### Writing plugins in C++
+When including headers from ClassiCube, they **must** be surrounded with `extern "C"`, i.e.
+```C
+extern "C" {
+#include "src/Chat.h"
+#include "src/Game.h"
+#include "src/String.h"
+}
+```
+Otherwise you will get obscure `Undefined reference` errors when compiling.
+
+Exported plugin functions **must** be surrounded with `extern "C"`, i.e.
+```C
+extern "C" {
+EXPORT int Plugin_ApiVersion = 1;
+EXPORT struct IGameComponent Plugin_Component = { TestPlugin_Init };
+}
+```
+Otherwise your plugin will not load. (you'll see `error getting plugin version` in-game)
+
+---
+
+## Compiling
+
+Plugin compilation instructions differs depending on the compiler and operating system
+
+### Linux
+
+[Compiling using gcc or clang](plugin-dev.md#using-gcc-or-clang)
+
+[Cross compiling for Windows 32-bit](plugin-dev.md#cross-compiling-for-windows-32-bit-using-mingw-w64)
+
+[Cross compiling for Windows 64-bit](plugin-dev.md#cross-compiling-for-windows-64-bit-using-mingw-w64)
+
+### macOS
+
+[Compiling using gcc or clang](plugin-dev.md#using-gcc-or-clang-1)
+
+### Windows
+
+[Compiling using Visual Studio](plugin-dev.md#using-visual-studio)
+
+[Compiling using mingw-w64](plugin-dev.md#using-mingw-w64)
+
+---
+
+## Compiling - Linux
+
+### Using gcc or clang
+
+#### Compiling
+
+`cc TestPlugin.c -o TestPlugin.so -shared -fPIC`
+
+Then put `TestPlugin.so` into your game's `plugins` folder. Done.
+
+### Cross compiling for Windows 32 bit using mingw-w64
+
+#### Setup
+
+1) Create `ClassiCube.exe` by either:
+    1) Compiling the game, see `Cross compiling for windows (32 bit)` in [main readme](/readme.md#cross-compiling-for-windows-32-bit)
+    2) Downloading 32 bit ClassiCube from https://www.classicube.net/download/#dl-win
+2) Install the `mingw-w64-tools` package (if it isn't already)
+3) Generate the list of exported symbols from `ClassiCube.exe` by running:
+    * `gendef ClassiCube.exe`
+4) Create a linkable library from the exported symbols list by running: 
+    * `i686-w64-mingw32-dlltool -d ClassiCube.def -l libClassiCube.a -D ClassiCube.exe`
+
+TODO: also document alternate method of compiling the game using --out-implib
+
+#### Compiling
+
+`i686-w64-mingw32-gcc TestPlugin.c -o TestPlugin.dll -s -shared -L . -lClassiCube`
+
+Then put `TestPlugin.dll` into your game's `plugins` folder. Done.
+
+### Cross compiling for Windows 64 bit using mingw-w64
+
+#### Setup
+
+1) Create `ClassiCube.exe` by either:
+    1) Compiling the game, see `Cross compiling for windows (64 bit)` in [main readme](/readme.md#cross-compiling-for-windows-64-bit)
+    2) Downloading 64 bit ClassiCube from https://www.classicube.net/download/#dl-win
+2) Install the `mingw-w64-tools` package (if it isn't already)
+3) Generate the list of exported symbols from `ClassiCube.exe` by running:
+    * `gendef ClassiCube.exe`
+4) Create a linkable library from the exported symbols list by running: 
+    * `x86_64-w64-mingw32-dlltool -d ClassiCube.def -l libClassiCube.a -D ClassiCube.exe`
+
+TODO: also document alternate method of compiling the game using --out-implib
+
+#### Compiling
+
+`x86_64-w64-mingw32-gcc TestPlugin.c -o TestPlugin.dll -s -shared -L . -lClassiCube`
+
+Then put `TestPlugin.dll` into your game's `plugins` folder. Done.
+
+## Compiling - macOS
+
+### Using gcc or clang
+
+#### Compiling
+
+`cc TestPlugin.c -o TestPlugin.dylib -undefined dynamic_lookup`
+
+Then put `TestPlugin.dylib` into your game's `plugins` folder. Done.
+
+## Compiling - Windows
+
+### Using Visual Studio
+TODO more detailed when I have some more time...
+
+#### Setup
+
+1) Compile the game, see `Compiling - Windows > using Visual Studio` in main readme
+2) Find the `ClassiCube.lib` that was generated when compiling the game. Usually it is in either `src\x64\Debug` or `src\x86\Debug`.
+3) Add a new `Empty Project` to the ClassiCube solution, then add the plugin .c files to it
+
+Note: If the plugin provides a .vcxproj file, you can skip step 2 and just open that project file instead.
+
+#### Configuration - alternative #1
+
+The simplest way of linking to the `.lib` file is simply adding the following code to one of the plugin's `.c` files
+
+```C
+#ifdef _MSC_VER
+  #ifdef _WIN64
+    #pragma comment(lib, "[GAME SRC FOLDER]/x64/Debug/ClassiCube.lib")
+  #else
+    #pragma comment(lib, "[GAME SRC FOLDER]/x86/Debug/ClassiCube.lib")
+  #endif
+#endif
+```
+
+replacing `[GAME SRC FOLDER]` with the full path of `src` folder (e.g. `C:/Dev/ClassiCube/src`)
+
+#### Configuration - alternative #2
+
+The more complicated way of linking to the `.lib` file is to add it to the plugin's project configuration file
+
+Right click the plugin project in the `Solution Explorer` pane, then click `Properties`
+
+TODO: add screenshots here
+
+TODO: may need to configure include directories
+
+1) In `Configuration properties` -> `General`, make sure `Configuration type` is set to `Dynamic library (.DLL)`
+
+2) In `Configuration properties` -> `Linker` -> `Input`, click the dropdown button for `Additional Dependencies`, then click `Edit`. Add the full path to `ClassiCube.lib`, then click `OK`
+
+#### Compiling
+
+Build the project. There should be a line in the build output that tells you where you can find the .dll file like this:
+`
+Project1.vcxproj -> C:\classicube-dev\testplugin\src\x64\Debug\TestPlugin.dll
+` 
+
+Then put `TestPlugin.dll` into your game's `plugins` folder. Done.
+
+### Using mingw-w64
+
+#### Setup
+
+1) Create `ClassiCube.exe` by either:
+    1) Compiling the game, see `Compiling for windows (MinGW-w64)` in [main readme](/readme.md#using-mingw-w64)
+    2) Downloading ClassiCube from https://www.classicube.net/download/#dl-win
+2) Generate the list of exported symbols in `ClassiCube.exe` by running:
+    * `gendef ClassiCube.exe`
+3) Create a linkable library from the exported symbols list by running: 
+    * `dlltool -d ClassiCube.def -l libClassiCube.a -D ClassiCube.exe`
+
+#### Compiling
+
+`gcc TestPlugin.c -o TestPlugin.dll -s -shared -L . -lClassiCube`
+
+Then put `TestPlugin.dll` into your game's `plugins` folder. Done.
+
+## Notes for compiling for Windows
+
+### Ensuring your plugin works when the ClassiCube exe isn't named ClassiCube.exe
+
+If you follow the prior compilation instructions, the compiled DLL will have a runtime dependancy on `ClassiCube.exe`
+
+However, this means that if the executable is e.g. named `ClassiCube (2).exe` instead. the plugin DLL will fail to load
+
+To avoid this problem, you must 
+1) Stop linking to `ClassiCube` (e.g. for `MinGW`, remove the ` -L . -lClassiCube`)
+2) Load all functions and variables exported from ClassiCube via `GetProcAddress` instead
+
+This is somewhat tedious to do - see [here](https://github.com/ClassiCube/ClassiCube-Plugins/) for some examples of plugins which do this
+
+#### Compiling ultra small plugin DLLs - MinGW
+If you **ONLY** use code from the game (no external libraries and no C standard library functions):
+* You can add `-nostartfiles -Wl,--entry=0` to the compile flags to reduce the DLL size (e.g from 11 to 4 kb)
+
+This isn't necessary to do though, and plugin DLLs work completely fine without doing this.
\ No newline at end of file
diff --git a/doc/portability.md b/doc/portability.md
new file mode 100644
index 0000000..3478a0a
--- /dev/null
+++ b/doc/portability.md
@@ -0,0 +1,135 @@
+Although most of the code is platform-independent, some per-platform functionality is required.
+
+By default I try to automatically define appropriate backends for your system in `Core.h`. Define ```CC_BUILD_MANUAL``` to disable this.
+
+## Before you start
+* IEEE floating support is required. (Can be emulated in software, but will affect performance)
+* int must be 32-bits. 32-bit addressing (or more) is required.
+* Support for 8/16/32/64 integer types is required. (your compiler must support 64-bit arithmetic)
+* At least around 2 MB of RAM is required at a minimum
+* At least 128 kb for main thread stack size
+
+In other words, the codebase can theroetically be ported to any modern-ish hardware, but not stuff like a UNIVAC machine, the SuperFX chip on the SNES, or an 8-bit microcontroller.
+
+## Supported platforms
+**Note:** Some of these machines are just virtual machines. Should still work on real hardware though.
+
+#### Tier 1 support
+These platforms are regularly tested on and have executables automatically compiled for. (see buildbot.sh)
+
+|Platform|Machine|Notes|
+|--------|-------|-----|
+|Windows x86/x64 | Windows 7 |
+|macOS x86/x64 | macOS 10.12 |
+|Linux x86/x64 | Xubuntu 14 | 
+|Web client | Chrome |
+
+#### Tier 2 support
+These machines are far less frequently tested on, but are otherwise same as tier 1 support.
+
+|Platform|Machine|Notes|
+|--------|-------|-----|
+|Windows x86 | Windows 2000 |
+|Windows x86 | 98 + KernelEX | Updating doesn't work
+|Windows x64 | Windows 10 |
+|ReactOS | ReactOS |
+|Linux x64 | Arch linux |
+|Linux x64 | Linux Mint |
+|Linux x64 | Kubuntu |
+|Linux x64 | Debian |
+|Linux x64 | Fedora |
+|Linux x86/x64 | Lubuntu |
+|Web client | Firefox |
+|Web client | Safari |
+|Web client | Edge | Cursor doesn't seem to disappear
+
+#### Tier 3 support
+The game has been compiled and run on these platforms before. It may or may not still compile for them.
+
+I don't really test these platforms at all, only when I suspect some changes to the code might impact them.
+
+|Platform|Machine|Notes|
+|--------|-------|-----|
+|macOS x86 | macOS 10.4 |
+|FreeBSD x86 | FreeBSD | x64 should work too |
+|NetBSD x86 | NetBSD | x64 should work too |
+|OpenBSD x86 | OpenBSD | x64 should work too |
+|Solaris x86 | OpenIndiana | x64 should work too |
+|macOS PPC | macOS 10.3 | PPC64 completely untested |
+|Linux PPC | Debian | Issues with colour channels incorrectly swapped? |
+|Linux ARM | Raspberry pi | ARM64 should work too |
+|Linux SPARC | Debian | Didn't really work due to lack of 24-bit colours |
+|Linux Alpha | Debian | 
+|HaikuOS | Nightly | 
+
+## Porting
+
+Listed below are the requirements for implementing each platform-dependent file.
+You should try to take advantage of existing backends when porting to other platforms.
+Only cross platform backends are listed below.
+
+### Platform
+General platform specific functionality.
+
+- Get exe path, start/exit process, open url in browser
+- Dynamic library management
+- Allocate, free, copy, set memory
+- Current system time, stopwatch time
+- File I/O, Directory I/O, Socket I/O
+- Threading, signalable wait, mutex
+- Drawing/measuring native font
+- Native font text drawing and measuring
+- Encrypt/decrypt data, getting command line args
+
+Define:
+- ```CC_BUILD_POSIX``` - Use posix API
+
+posix note: Some functions are not covered. (stopwatch, getting exe path, open url in browser)
+These must still be implemented for each operating system
+
+### Window
+Create a window, show a dialog window, set window contents, keyboard/mouse input
+
+Also monitor size, clipboard, cursor, raw relative mouse movement (optional)
+
+Define:
+- ```CC_BUILD_X11``` - Use X11/XLib (unix-ish) (glX)
+- ```CC_BUILD_SDL``` - Use SDL library (SDL)
+
+If using OpenGL, also OpenGL context management
+
+### Logger
+Dump registers and backtrace, log unhandled errors (e.g. invalid memory read)
+
+Define:
+- ```CC_BUILD_POSIX``` - use POSIX api
+
+posix note: Register access is highly dependent on OS and architecture.
+
+(e.g. Linux uses &r.gregs[REG_EAX] while FreeBSD uses &r.mc_eax)
+
+### Audio
+Play multiple audio streams with varying sample rates
+
+Define:
+- ```CC_BUILD_OPENAL``` - use OpenAL
+- ```CC_BUILD_NOAUDIO``` - stub audio implementation (silent)
+
+### 3D Graphics
+Texturing, depth buffer, alpha, etc (See Graphics.h for full list)
+
+Define:
+- ```CC_BUILD_D3D9``` - Use Direct3D9
+- ```CC_BUILD_GL``` - Use OpenGL (1.5/1.2 + ARB_VERTEX_BUFFER_OBJECT)
+- ```CC_BUILD_GL11``` - Use OpenGL 1.1 features only
+- ```CC_BUILD_GLMODERN``` - Use modern OpenGL shaders
+- ```CC_BUILD_GLES``` - Makes these shaders compatible with OpenGL ES
+
+### HTTP
+HTTP, HTTPS, and setting request/getting response headers
+
+Define:
+- ```CC_BUILD_HTTPCLIENT``` - use built in simple HTTP backend
+- ```CC_BUILD_CURL``` - use libcurl for HTTP
+
+Supporting connection reuse is highly recommended. (but not required)
diff --git a/doc/readme.md b/doc/readme.md
new file mode 100644
index 0000000..547826e
--- /dev/null
+++ b/doc/readme.md
@@ -0,0 +1,12 @@
+This folder contains general information relating to the game's source code.
+
+|File|Description|
+|--------|-------|
+|compile-fixes.md | Steps on how to fix some common compilation errors |
+|hosting-flask.md | Example website that hosts the web client using [Flask](https://flask.palletsprojects.com/)|
+|hosting-webclient.md | Explains how to integrate the web client into your own website | 
+|modules.md | Provides a summary about the modules that constitute the game's code|
+|plugin-dev.md | Explains how to compile a simple plugin for the game |
+|portability.md | Provides information about porting this game to other platforms |
+|style.md | Explains the style guidelines that the source code generally follows |
+|strings.md | Provides details about the custom string type used by this game |
\ No newline at end of file
diff --git a/doc/sound-credits.md b/doc/sound-credits.md
new file mode 100644
index 0000000..30239fc
--- /dev/null
+++ b/doc/sound-credits.md
@@ -0,0 +1,42 @@
+## Sources
+
+https://old.reddit.com/r/Minecraft/comments/3zxucv/where_did_the_sound_in_minecraft_come_from/
+https://web.archive.org/web/20100830062601/http://minecraft.net/credits.jsp
+https://web.archive.org/web/20100124101348/http://www.freesound.org/usersAttribution.php?id=1045860&format=html
+https://web.archive.org/web/20100613143900/http://www.freesound.org/usersAttribution.php?id=584268
+https://freesound.org/people/C418/downloaded_sounds/?page=7#sound
+
+## Sounds
+
+**dig/step_grass1** - grass1.wav by Snoman
+* https://freesound.org/people/Snoman/sounds/9904/
+
+**dig/step_grass2** - grass2.wav by Snoman
+* https://freesound.org/people/Snoman/sounds/9905/
+
+**dig/step_grass3** - grass3.wav by Snoman
+* https://freesound.org/people/Snoman/sounds/9906/
+
+**dig/step_grass4** - grass4.wav by Snoman
+* https://freesound.org/people/Snoman/sounds/9907/
+
+**dig_glass1** - glass shatter.wav by datasoundsample
+* https://freesound.org/people/datasoundsample/sounds/41348/
+
+**dig_glass2** - gb12.aif by lsprice
+* http://www.freesound.org/samplesViewSingle.php?id=88808/
+
+**dig_glass3** - bm_Glass_Break.wav by cmusounddesign
+* https://freesound.org/people/cmusounddesign/sounds/71947/
+
+**dig/step_gravel1** - gravel walking.aif by tigersound
+* https://freesound.org/people/tigersound/sounds/15562/
+
+**dig/step_gravel2** - gravel walking.aif by tigersound
+* https://freesound.org/people/tigersound/sounds/15562/
+
+**dig/step_gravel3** - gravel walking.aif by tigersound
+* https://freesound.org/people/tigersound/sounds/15562/
+
+**dig/step_gravel4** - gravel walking.aif by tigersound
+* https://freesound.org/people/tigersound/sounds/15562/
\ No newline at end of file
diff --git a/doc/strings.md b/doc/strings.md
new file mode 100644
index 0000000..9fa905a
--- /dev/null
+++ b/doc/strings.md
@@ -0,0 +1,329 @@
+## Introduction
+
+ClassiCube uses a custom string type rather than the standard C `char*` string in most places
+
+ClassiCube strings (`cc_string`) are a struct with the following fields:
+- `buffer` -> Pointer to 8 bit characters (unsigned [code page 437 indices](https://en.wikipedia.org/wiki/Code_page_437#Character_set))
+- `length` -> Number of characters currently used
+- `capacity` -> Maximum number of characters (i.e buffer size)
+
+Note: This means **STRINGS MAY NOT BE NULL TERMINATED** (and are not in most cases)
+
+You should also read the **Strings** section in the [style guide](/doc/style.md)
+
+## Memory management
+Some general guidelines to keep in mind when it comes to `cc_string` strings:
+- String buffers can be allocated on either the stack or heap<br>
+(i.e. make sure you don't return strings that are using stack allocated buffers)
+- Strings are fixed capacity (strings do not grow when length reaches capcity)<br>
+(i.e. make sure you allocate a large enough buffer upfront)
+- Strings are not garbage collected or reference counted<br>
+(i.e. you are responsible for managing the lifetime of strings)
+
+## Usage examples
+
+Initialisating a string from readonly text:
+```C
+cc_string str = String_FromConst("ABC");
+```
+
+Initialising a string from temporary memory on the stack:
+```C
+// str will be able to store at most 200 characters in it
+char strBuffer[200];
+cc_string str = String_FromArray(strBuffer);
+```
+
+Initialising a string from persistent memory on the heap:
+```C
+// str will be able to store at most 200 characters in it
+char* str = Mem_Alloc(1, 200, "String buffer");
+cc_string str = String_Init(str, 0, 200);
+```
+
+# Converting to/from other string representations
+
+## C String conversion
+
+### C string -> cc_string
+
+Creating a `cc_string` string from a C string is straightforward:
+
+#### From a constant C string
+```C
+void Example(void) {
+    cc_string str = String_FromConst("test");
+}
+```
+
+#### From a C string
+```C
+void Example(const char* c_str) {
+    cc_string str = String_FromReadonly(c_str);
+}
+```
+Note: `String_FromReadonly` can also be used with constant C strings, it's just a bit slower
+
+#### From a C fixed size string
+```C
+struct Something { int value; char name[50]; };
+
+void Example(struct Something* some) {
+    cc_string str = String_FromRawArray(some->name);
+}
+```
+
+### cc_string -> C string
+
+The `buffer` field **should not** be treated as a C string, because `cc_string` strings **MAY NOT BE NULL TERMINATED** 
+
+The general way to achieve this is to
+1. Initialise `capacity` with 1 less than actual buffer size (e.g. use `String_InitArray_NT` instead of `String_InitArray`)
+2. Perform various operations on the `cc_string` string
+3. Add null terminator to end (i.e. `buffer[length]` = '\0';
+4. Use `buffer` as a C string now
+
+For example:
+```C
+void PrintInt(int value) {
+    cc_string str; char strBuffer[128];
+    String_InitArray_NT(str, strBuffer);
+    String_AppendInt(&str, value);
+    str.buffer[str.length] = '\0';
+    puts(str.buffer);
+}
+```
+
+## OS String conversion
+
+`cc_string` strings cannot be directly used as arguments for operating system functions and must be converted first.
+
+The following functions are provided to convert `cc_string` strings into operating system specific encoded strings:
+
+### cc_string -> Windows string
+
+`Platform_EncodeString` converts a `cc_string` into a null terminated `WCHAR` and `CHAR` string
+
+#### Example
+```C
+void SetWorkingDir(cc_string* title) {
+    cc_winstring str;
+    Platform_EncodeUtf16(&str, title);
+    SetCurrentDirectoryW(str.uni);
+	
+    // it's recommended that you DON'T use the ansi format whenever possible
+    //SetCurrentDirectoryA(str.ansi); 
+}
+```
+
+### cc_string -> UTF8 string
+
+`String_EncodeUtf8` converts a `cc_string` into a null terminated UTF8-encoded `char*` string
+
+#### Example
+```C
+void SetWorkingDir(cc_string* title) {
+    char buffer[NATIVE_STR_LEN];
+    String_EncodeUtf8(buffer, title);
+    chdir(buffer);
+}
+```
+
+# API
+
+I'm lazy so I will just link to [String.h](/src/String.h)
+
+If you'd rather I provided a more detailed reference here, please let me know.
+
+TODO
+
+# Comparisons to other string implementations
+
+## C comparison
+
+A rough mapping of C string API to ClassiCube's string API:
+```
+atof    -> Convert_ParseFloat
+strtof  -> Convert_ParseFloat
+atoi    -> Convert_ParseInt
+strtoi  -> Convert_ParseInt
+
+strcat  -> String_AppendConst/String_AppendString
+strcpy  -> String_Copy
+strtok  -> String_UNSAFE_Split
+
+strlen  -> str.length
+strcmp  -> String_Equals/String_Compare
+strchr  -> String_IndexOf
+strrchr -> String_LastIndexOf
+strstr  -> String_IndexOfConst
+
+sprintf -> String_Format1/2/3/4
+    %d  -> %i
+  %04d -> %p4
+    %i  -> %i
+    %c  -> %r
+  %.4f  -> %f4
+    %s  -> %s (cc_string)
+    %s  -> %c (char*)
+    %x  -> %h
+```
+
+## C# comparison
+
+A rough mapping of C# string API to ClassiCube's string API:
+```
+byte.Parse   -> Convert_ParseUInt8
+ushort.Parse -> Convert_ParseUInt16
+float.Parse  -> Convert_ParseFloat
+int.Parse    -> Convert_ParseInt
+ulong.Parse  -> Convert_ParseUInt64
+bool.Parse   -> Convert_ParseBool
+
+a += "X";     -> String_AppendString
+b = a;        -> String_Copy
+string.Insert -> String_InsertAt
+string.Remove -> String_DeleteAt
+
+string.Substring -> String_UNSAFE_Substring/String_UNSAFE_SubstringAt
+string.Split     -> String_UNSAFE_Split/String_UNSAFE_SplitBy
+string.TrimStart -> String_UNSAFE_TrimStart
+string.TrimEnd   -> String_UNSAFE_TrimEnd
+
+a.Length           -> str.length
+a == b             -> String_Equals
+string.Equals      -> String_CaslessEquals (StringComparison.OrdinalIgnoreCase)
+string.IndexOf     -> String_IndexOf/String_IndexOfConst
+string.LastIndexOf -> String_LastIndexOf
+string.StartsWith  -> String_CaselessStarts (StringComparison.OrdinalIgnoreCase)
+string.EndsWith    -> String_CaselessEnds   (StringComparison.OrdinalIgnoreCase)
+string.CompareTo   -> String_Compare
+
+string.Format -> String_Format1/2/3/4
+```
+*Note: I modelled cc_string after C# strings, hence the similar function names*
+
+## C++ comparison
+
+A rough mapping of C++ std::string API to ClassiCube's string API:
+```
+std::stof  -> Convert_ParseFloat
+std::stoi  -> Convert_ParseInt
+std::stoul -> Convert_ParseUInt64
+
+string::append -> String_AppendString/String_AppendConst
+b = a;         -> String_Copy
+string::insert -> String_InsertAt
+string::erase  -> String_DeleteAt
+
+string::substr -> String_UNSAFE_Substring/String_UNSAFE_SubstringAt
+
+string::length  -> str.length
+a == b          -> String_Equals
+string::find    -> String_IndexOf/String_IndexOfConst
+string::rfind   -> String_LastIndexOf
+string::compare -> String_Compare
+
+std::sprintf -> String_Format1/2/3/4
+```
+
+# Detailed lifetime examples
+
+Managing the lifetime of strings is important, as not properly managing them can cause issues.
+
+For example, consider the following function:
+```C
+const cc_string* GetString(void);
+
+void PrintSomething(void) {
+	cc_string* str = GetString();
+	// .. other code ..
+	Chat_Add(str);
+}
+```
+
+Without knowing the lifetime of the string returned from `GetString`, using it might either:
+* Work just fine
+* Sometimes work fine
+* Cause a subtle issue
+* Cause a major problem
+ptodo rearrange
+
+### Constant string return example
+```C
+const cc_string* GetString(void) {
+	static cc_string str = String_FromConst("ABC");
+	return &str;
+}
+```
+
+This will work fine - as long as the caller does not modify the returned string at all
+
+### Stack allocated string return example
+
+```C
+const cc_string* GetString(void) {
+	char strBuffer[1024];
+	cc_string str = String_FromArray(strBuffer);
+	
+	String_AppendConst(&str, "ABC");
+	return &str;
+}
+```
+
+This will **almost certainly cause problems** - after `GetString` returns, the contents of both `str` and `strBuffer` may be changed to arbitary values (as once `GetString` returns, their contents are then eligible to be overwritten by other stack allocated variables)
+
+As a general rule, you should **NEVER** return a string allocated on the stack
+
+### Dynamically allocated string return example
+
+```C
+const cc_string* GetString(void) {
+	char* buffer   = Mem_Alloc(1024, 1, "string buffer");
+	cc_string* str = Mem_Alloc(1, sizeof(cc_string), "string"); 
+	
+	*str = String_Init(buffer, 0, 1024);
+	String_AppendConst(str, "ABC");
+	return str;
+}
+```
+
+This will work fine - however, now you also need to remember to `Mem_Free` both the string and its buffer to avoid a memory leak
+
+As a general rule, you should avoid returning a dynamically allocated string
+
+### UNSAFE mutable string return example
+
+```C
+char global_buffer[1024];
+cc_string global_str = String_FromArray(global_buffer);
+
+const cc_string* GetString(void) {
+	return &global_str;
+}
+```
+
+Depending on what functions are called in-between `GetString` and `Chat_Add`, `global_str` or its contents may be modified - which can result in an unexpected value being displayed in chat
+
+This potential issue is not just theoretical - it has actually resulted in several real bugs in ClassiCube itself
+
+As a general rule, for unsafe functions returning a string that may be mutated behind your back, you should try to maintain a reference to the string for as short of time as possible
+
+### Reducing string lifetime issues
+
+In general, for functions that produce strings, you should try to leave the responsibility of managing the string's lifetime up to the calling function to avoid these pitfalls
+
+The example from before could instead be rewritten like so:
+
+```C
+void GetString(cc_string* str);
+
+void PrintSomething(void) {
+	char strBuffer[256];
+	cc_string str = String_InitArray(strBuffer);
+	GetString(&str);
+	
+	// .. other code ..
+	Chat_Add(&str);
+}
+```
\ No newline at end of file
diff --git a/doc/style.md b/doc/style.md
new file mode 100644
index 0000000..3bc316f
--- /dev/null
+++ b/doc/style.md
@@ -0,0 +1,34 @@
+### Guidelines
+* Code should be C89 compatible and compilable as C++.
+* Each .c file should represent a module. (see architecture.md for more details)
+* Public functions and variables should be prefixed by module to avoid name collisions. (e.g. `Game_Reset`)
+* Private functions should be named using pascal case. Prefixing module is optional - do it when it makes sense.
+* Private variables don't really have a consistent style.
+
+### Types
+* Explicit integer size typedefs are provided in `Core.h` for when needed. Otherwise just use int.
+* A few common simple structs are typedef-ed, but are rarely otherwise.
+* `cc_bool` is an alias for 8 bit unsigned integer
+* `PackedCol` field order differs depending on the underlying 3D graphics API
+
+Note: The explicit integer size typedefs may not have been defined if you aren't compiling using GCC/Clang/MSVC, so for other compilers you may need to add them into `Core.h`
+
+### Strings
+
+A custom string type (`cc_string`) is used rather than `char*` strings in most places (see [strings](strings.md) page for more details)
+
+*Note: Several functions will take raw `char*` for performance, but this is not encouraged*
+
+#### String arguments
+String arguments are annotated to indicate storage and readonly requirements. These are:
+- `const cc_string*` - String is not modified at all
+- `cc_string*` - Characters in string may be modified
+- `STRING_REF` - Macro annotation indicating a **reference is kept to the characters**
+
+To make it extra clear, functions with `STRING_REF` arguments usually also have `_UNSAFE_` as part of their name.
+
+For example, consider the function `cc_string Substring_UNSAFE(STRING_REF const cc_string* str, length)`
+
+The *input string* is not modified at all. However, the characters of the *returned string* points to the characters of the *input string*, so modifying the characters in the *input string* also modifies the *returned string*.
+
+In general, use of `const cc_string*` is preferred when possible, and `STRING_REF` as little as possible.