diff --git a/src/auth.c b/src/auth.c index 4ff781580..0636b6d2c 100644 --- a/src/auth.c +++ b/src/auth.c @@ -7,6 +7,14 @@ #define MAX_POST_LENGTH 1024 #define MAX_RESPONSE_LENGTH 1024 +// Write function callback for curl +// Arguments: +// - data: data source pointer +// - size: size of each data element +// - count: number of data elements +// - arg +// Returns: +// - an integer value size_t write_function(char *data, size_t size, size_t count, void *arg) { size_t length = size * count; char *dst = (char *)arg; @@ -18,6 +26,15 @@ size_t write_function(char *data, size_t size, size_t count, void *arg) { return length; } +// Get access token +// Arguments: +// - result: output pointer +// - length: maximum data length to use in result pointer +// - username: string username to authenticate +// - identity_token: string id token to authenticate +// Returns: +// - non-zero upon success +// - writes response data to result int get_access_token( char *result, int length, char *username, char *identity_token) { @@ -47,3 +64,4 @@ int get_access_token( } return 0; } + diff --git a/src/client.c b/src/client.c index 8bea4f708..d16342c6b 100644 --- a/src/client.c +++ b/src/client.c @@ -14,12 +14,18 @@ #include "client.h" #include "tinycthread.h" +// "QUEUE_SIZE" is the number of chars in the send queue #define QUEUE_SIZE 1048576 #define RECV_SIZE 4096 +// Client state (not available to outside code) + static int client_enabled = 0; static int running = 0; + +// Socket descriptor static int sd = 0; + static int bytes_sent = 0; static int bytes_received = 0; static char *queue = 0; @@ -27,18 +33,36 @@ static int qsize = 0; static thrd_t recv_thread; static mtx_t mutex; +// Sets the client state to be enabled. +// Arguments: none +// Returns: none void client_enable() { client_enabled = 1; } +// Sets the client state to be disabled. +// Arguments: none +// Returns: none void client_disable() { client_enabled = 0; } +// Get whether the client is enabled or not. +// Arguments: none +// Returns: +// - boolean (non-zero) if the client is enabled int get_client_enabled() { return client_enabled; } +// Send all data socket descriptor. +// Not meant to usually be called directly, but meant to be called by client_send(). +// Arguments: +// - sd: socket descriptor to send data through +// - data: string data to send +// - length: length of the string data +// Returns: +// - 0 upon completion/success int client_sendall(int sd, char *data, int length) { if (!client_enabled) { return 0; @@ -56,6 +80,10 @@ int client_sendall(int sd, char *data, int length) { return 0; } +// Client send a data string +// Arguments: +// - data +// Returns: none void client_send(char *data) { if (!client_enabled) { return; @@ -66,6 +94,10 @@ void client_send(char *data) { } } +// Client send version +// Arguments: +// - version +// Returns: none void client_version(int version) { if (!client_enabled) { return; @@ -75,6 +107,11 @@ void client_version(int version) { client_send(buffer); } +// Client send authenticate login with identity +// Arguments: +// - username +// - identity_token +// Returns: none void client_login(const char *username, const char *identity_token) { if (!client_enabled) { return; @@ -84,6 +121,14 @@ void client_login(const char *username, const char *identity_token) { client_send(buffer); } +// Client send player position +// Arguments: +// - x +// - y +// - z +// - rx +// - ry +// Returns: none void client_position(float x, float y, float z, float rx, float ry) { if (!client_enabled) { return; @@ -104,6 +149,12 @@ void client_position(float x, float y, float z, float rx, float ry) { client_send(buffer); } +// Client send request for chunk +// Arguments: +// - p +// - q +// - key +// Returns: none void client_chunk(int p, int q, int key) { if (!client_enabled) { return; @@ -113,6 +164,13 @@ void client_chunk(int p, int q, int key) { client_send(buffer); } +// Client send block update +// Arguments: +// - x +// - y +// - z +// - w +// Returns: none void client_block(int x, int y, int z, int w) { if (!client_enabled) { return; @@ -122,6 +180,13 @@ void client_block(int x, int y, int z, int w) { client_send(buffer); } +// Client send lighting update +// Arguments: +// - x +// - y +// - z +// - w +// Returns: none void client_light(int x, int y, int z, int w) { if (!client_enabled) { return; @@ -131,6 +196,14 @@ void client_light(int x, int y, int z, int w) { client_send(buffer); } +// Client send sign creation +// Arguments: +// - x +// - y +// - z +// - face +// - text +// Returns: none void client_sign(int x, int y, int z, int face, const char *text) { if (!client_enabled) { return; @@ -140,6 +213,10 @@ void client_sign(int x, int y, int z, int face, const char *text) { client_send(buffer); } +// Client send chat message +// Arguments: +// - text +// Returns: none void client_talk(const char *text) { if (!client_enabled) { return; @@ -152,6 +229,10 @@ void client_talk(const char *text) { client_send(buffer); } +// Client receive data +// Arguments: none +// Returns: +// - ? char *client_recv() { if (!client_enabled) { return 0; @@ -176,6 +257,11 @@ char *client_recv() { return result; } +// Receive worker +// Arguments: +// - arg +// Returns: +// - ? int recv_worker(void *arg) { char *data = malloc(sizeof(char) * RECV_SIZE); while (1) { @@ -209,10 +295,17 @@ int recv_worker(void *arg) { return 0; } +// Client connect to server +// Note: this is where the socket descriptor "sd" is initialized. +// Arguments: +// - hostname +// - port +// Returns: none void client_connect(char *hostname, int port) { if (!client_enabled) { return; } + // Get host address struct hostent *host; struct sockaddr_in address; if ((host = gethostbyname(hostname)) == 0) { @@ -223,21 +316,27 @@ void client_connect(char *hostname, int port) { address.sin_family = AF_INET; address.sin_addr.s_addr = ((struct in_addr *)(host->h_addr_list[0]))->s_addr; address.sin_port = htons(port); + // Create socket if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } + // Connect if (connect(sd, (struct sockaddr *)&address, sizeof(address)) == -1) { perror("connect"); exit(1); } } +// Start the client. +// Arguments: none +// Returns: none void client_start() { if (!client_enabled) { return; } running = 1; + // Create the queue queue = (char *)calloc(QUEUE_SIZE, sizeof(char)); qsize = 0; mtx_init(&mutex, mtx_plain); @@ -247,6 +346,9 @@ void client_start() { } } +// Stop the client. +// Arguments: none +// Returns: none void client_stop() { if (!client_enabled) { return; @@ -263,3 +365,4 @@ void client_stop() { // printf("Bytes Sent: %d, Bytes Received: %d\n", // bytes_sent, bytes_received); } + diff --git a/src/cube.c b/src/cube.c index db23a88fe..ac624d600 100644 --- a/src/cube.c +++ b/src/cube.c @@ -4,12 +4,36 @@ #include "matrix.h" #include "util.h" +// Make a complete cube model. +// Arguments: +// - data: output pointer +// - ao +// - light +// - left: whether to generate a left face +// - right: whether to generate a right face +// - top: whether to generate a top face +// - bottom: whether to generate a bottom face +// - front: whether to generate a front face +// - back: whether to generate a back face +// - wleft: texture tile number for left face +// - wright: texture tile number for right face +// - wtop: texture tile number for top face +// - wbottom: texture tile number for bottom face +// - wfront: texture tile number for front face +// - wback: texture tile number for back face +// - x: block x center position +// - y: block y center position +// - z: block z center position +// - n: cube scale; the distance of each cube face from the cube center +// Returns: +// - writes specific values to the data pointer void make_cube_faces( float *data, float ao[6][4], float light[6][4], int left, int right, int top, int bottom, int front, int back, int wleft, int wright, int wtop, int wbottom, int wfront, int wback, float x, float y, float z, float n) { + // 6 faces each with 4 points, each of which are 3-vectors static const float positions[6][4][3] = { {{-1, -1, -1}, {-1, -1, +1}, {-1, +1, -1}, {-1, +1, +1}}, {{+1, -1, -1}, {+1, -1, +1}, {+1, +1, -1}, {+1, +1, +1}}, @@ -18,6 +42,7 @@ void make_cube_faces( {{-1, -1, -1}, {-1, +1, -1}, {+1, -1, -1}, {+1, +1, -1}}, {{-1, -1, +1}, {-1, +1, +1}, {+1, -1, +1}, {+1, +1, +1}} }; + // 6 faces each with a 3-vector normal direction static const float normals[6][3] = { {-1, 0, 0}, {+1, 0, 0}, @@ -26,6 +51,7 @@ void make_cube_faces( {0, 0, -1}, {0, 0, +1} }; + // 6 faces each with 4 points, each of which are 2-vectors static const float uvs[6][4][2] = { {{0, 0}, {1, 0}, {0, 1}, {1, 1}}, {{1, 0}, {0, 0}, {1, 1}, {0, 1}}, @@ -42,6 +68,7 @@ void make_cube_faces( {0, 3, 2, 0, 1, 3}, {0, 3, 1, 0, 2, 3} }; + // flipped indices static const float flipped[6][6] = { {0, 1, 2, 1, 3, 2}, {0, 2, 1, 2, 3, 1}, @@ -51,26 +78,34 @@ void make_cube_faces( {0, 2, 1, 2, 3, 1} }; float *d = data; + // Scale the texture atlas so that a 16x16 tile takes up the whole block float s = 0.0625; float a = 0 + 1 / 2048.0; float b = s - 1 / 2048.0; int faces[6] = {left, right, top, bottom, front, back}; int tiles[6] = {wleft, wright, wtop, wbottom, wfront, wback}; for (int i = 0; i < 6; i++) { + // i is an index into the faces + // Do not write to faces which were not specified. if (faces[i] == 0) { continue; } + // Convert texture tile number to texture pixel coordinates float du = (tiles[i] % 16) * s; float dv = (tiles[i] / 16) * s; int flip = ao[i][0] + ao[i][3] > ao[i][1] + ao[i][2]; for (int v = 0; v < 6; v++) { + // j is index into the points int j = flip ? flipped[i][v] : indices[i][v]; + // Write the position 3-vector *(d++) = x + n * positions[i][j][0]; *(d++) = y + n * positions[i][j][1]; *(d++) = z + n * positions[i][j][2]; + // Write the normal 3-vector *(d++) = normals[i][0]; *(d++) = normals[i][1]; *(d++) = normals[i][2]; + // Write the texture UV 2-vector *(d++) = du + (uvs[i][j][0] ? b : a); *(d++) = dv + (uvs[i][j][1] ? b : a); *(d++) = ao[i][j]; @@ -79,11 +114,30 @@ void make_cube_faces( } } +// Make a cube model for a block +// Arguments: +// - data: output pointer +// - ao +// - light +// - left: whether to generate a left face +// - right: whether to generate a right face +// - top: whether to generate a top face +// - bottom: whether to generate a bottom face +// - front: whether to generate a front face +// - back: whether to generate a back face +// - x: block x center position +// - y: block y center position +// - z: block z center position +// - n: cube scale; the distance of each face from the cube center +// - w: block id to use for textures +// Returns: +// - writes specific values to the data pointer void make_cube( float *data, float ao[6][4], float light[6][4], int left, int right, int top, int bottom, int front, int back, float x, float y, float z, float n, int w) { + // Get blocks texture faces (the blocks lookup table is defined in item.c) int wleft = blocks[w][0]; int wright = blocks[w][1]; int wtop = blocks[w][2]; @@ -97,6 +151,19 @@ void make_cube( x, y, z, n); } +// Make a plant model +// Arguments: +// - data: output pointer +// - ao +// - light +// - px: +// - py: +// - pz: +// - n: +// - w: +// - rotation: +// Returns: +// - writes specific values to the data pointer void make_plant( float *data, float ao, float light, float px, float py, float pz, float n, int w, float rotation) @@ -126,26 +193,34 @@ void make_plant( {0, 3, 1, 0, 2, 3} }; float *d = data; + // Scale the texture atlas so that a 16x16 tile takes up the whole block float s = 0.0625; float a = 0; float b = s; + // Convert texture tile number to texture pixel coordinates float du = (plants[w] % 16) * s; float dv = (plants[w] / 16) * s; for (int i = 0; i < 4; i++) { for (int v = 0; v < 6; v++) { int j = indices[i][v]; + // Write the position 3-vector *(d++) = n * positions[i][j][0]; *(d++) = n * positions[i][j][1]; *(d++) = n * positions[i][j][2]; + // Write the normal 3-vector *(d++) = normals[i][0]; *(d++) = normals[i][1]; *(d++) = normals[i][2]; + // Write the UV 2-vector *(d++) = du + (uvs[i][j][0] ? b : a); *(d++) = dv + (uvs[i][j][1] ? b : a); + // Write the ao value *(d++) = ao; + // Write the light value *(d++) = light; } } + // matrix "ma" and matrix "mb" float ma[16]; float mb[16]; mat_identity(ma); @@ -157,6 +232,16 @@ void make_plant( mat_apply(data, ma, 24, 0, 10); } +// Make a player model +// Arguments: +// - data: output pointer +// - x: +// - y: +// - z: +// - rx: +// - ry: +// Returns: +// - writes specific values to the data pointer void make_player( float *data, float x, float y, float z, float rx, float ry) @@ -170,6 +255,8 @@ void make_player( {0.8, 0.8, 0.8, 0.8}, {0.8, 0.8, 0.8, 0.8} }; + // Make a player head with specific texture tiles + // and a scale smaller than a normal block make_cube_faces( data, ao, light, 1, 1, 1, 1, 1, 1, @@ -188,7 +275,17 @@ void make_player( mat_apply(data, ma, 36, 0, 10); } +// Make a cube wireframe model +// Arguments: +// - data: output pointer (must have room for 24*3 integers) +// - x: block x position +// - y: block y position +// - z: block z position +// - n: cube scale; the distance of each cube face from the cube center +// Returns: +// - writes specific values to the data pointer void make_cube_wireframe(float *data, float x, float y, float z, float n) { + // 8 points, each of which are 3-vectors static const float positions[8][3] = { {-1, -1, -1}, {-1, -1, +1}, @@ -199,6 +296,7 @@ void make_cube_wireframe(float *data, float x, float y, float z, float n) { {+1, +1, -1}, {+1, +1, +1} }; + // The correct ordering of the points to form a nice cube static const int indices[24] = { 0, 1, 0, 2, 0, 4, 1, 3, 1, 5, 2, 3, 2, 6, 3, 7, @@ -207,12 +305,23 @@ void make_cube_wireframe(float *data, float x, float y, float z, float n) { float *d = data; for (int i = 0; i < 24; i++) { int j = indices[i]; + // Write the position 3-vector *(d++) = x + n * positions[j][0]; *(d++) = y + n * positions[j][1]; *(d++) = z + n * positions[j][2]; } } +// Make a rectangle for a 2D text character. +// Arguments: +// - data: output pointer +// - x: character center x +// - y: character center y +// - n: character width (from center to edge) +// - m: character height (from center to edge) +// - c: ASCII character value +// Returns: +// - data: output pointer void make_character( float *data, float x, float y, float n, float m, char c) @@ -238,6 +347,17 @@ void make_character( *(d++) = du + 0; *(d++) = dv + b; } +// Make a rectangle for a 3D text character (for rendering signs). +// Arguments: +// - data: output pointer +// - x: character center x +// - y: character center y +// - z: character center z +// - n: scale +// - face: which cube face the text is on +// - c: ASCII character value +// Returns: +// - data: output pointer void make_character_3d( float *data, float x, float y, float z, float n, int face, char c) { @@ -297,6 +417,8 @@ void make_character_3d( } } +// Make a sphere with radius r and the given level of detail. +// NOTE: Meant to be called recursively and by make_sphere(). int _make_sphere( float *data, float r, int detail, float *a, float *b, float *c, @@ -343,16 +465,24 @@ int _make_sphere( } } +// Make a sphere with radius r and the given level of detail. +// Arguments: +// - data: output pointer +// - r: sphere radius +// - detail: level of detail (see note below) +// Returns: +// - data: output pointer +// NOTE: Table of resources needed for each level of detail: +// detail, triangles, floats +// 0, 8, 192 +// 1, 32, 768 +// 2, 128, 3072 +// 3, 512, 12288 +// 4, 2048, 49152 +// 5, 8192, 196608 +// 6, 32768, 786432 +// 7, 131072, 3145728 void make_sphere(float *data, float r, int detail) { - // detail, triangles, floats - // 0, 8, 192 - // 1, 32, 768 - // 2, 128, 3072 - // 3, 512, 12288 - // 4, 2048, 49152 - // 5, 8192, 196608 - // 6, 32768, 786432 - // 7, 131072, 3145728 static int indices[8][3] = { {4, 3, 0}, {1, 4, 0}, {3, 4, 5}, {4, 1, 5}, @@ -382,3 +512,4 @@ void make_sphere(float *data, float r, int detail) { total += n; data += n * 24; } } + diff --git a/src/db.c b/src/db.c index cd883a858..ba7f3dadc 100644 --- a/src/db.c +++ b/src/db.c @@ -4,6 +4,9 @@ #include "sqlite3.h" #include "tinycthread.h" +// Database code to save and load worlds. +// Only player-made changes from the original generated world are saved in the db. + static int db_enabled = 0; static sqlite3 *db; @@ -24,18 +27,37 @@ static mtx_t mtx; static cnd_t cnd; static mtx_t load_mtx; +// Enable the database +// (Used because the variable db_enabled is private to this file). +// Arguments: none +// Returns: none void db_enable() { db_enabled = 1; } +// Disable the database +// (Used because the variable db_enabled is private to this file). +// Arguments: none +// Returns: none void db_disable() { db_enabled = 0; } +// Get whether the database is enabled or not. +// (Used because the variable db_enabled is private to this file). +// Arguments: none +// Returns: +// - whether the database is enabled int get_db_enabled() { return db_enabled; } +// Initialize a database stored in the a file with the given path (file may or may not exist). +// If the file exists, this creates each database table only if it does not already exist. +// Arguments: +// - path: path to database file to use or create if non-existent +// Returns: +// - non-zero if there was a database error int db_init(char *path) { if (!db_enabled) { return 0; @@ -150,6 +172,9 @@ int db_init(char *path) { return 0; } +// Close the database and save pending commits +// Arguments: none +// Returns: none void db_close() { if (!db_enabled) { return; @@ -169,6 +194,9 @@ void db_close() { sqlite3_close(db); } +// Let one of the workers do the database commit. +// Arguments: none +// Returns: none void db_commit() { if (!db_enabled) { return; @@ -179,6 +207,9 @@ void db_commit() { mtx_unlock(&mtx); } +// Actually do a database commit. +// Arguments: none +// Returns: none void _db_commit() { sqlite3_exec(db, "commit; begin;", NULL, NULL, NULL); } @@ -273,6 +304,13 @@ int db_auth_get_selected( return result; } +// Save the player state to the database. +// Arguments: +// - x: x position to save +// - y: y position to save +// - z: z position to save +// - rx: rotation x to save +// - ry: rotation y to save void db_save_state(float x, float y, float z, float rx, float ry) { if (!db_enabled) { return; @@ -291,6 +329,15 @@ void db_save_state(float x, float y, float z, float rx, float ry) { sqlite3_finalize(stmt); } +// Load the player state from the database. +// Arguments: +// - x: pointer to x position to load value into +// - y: pointer to y position to load value into +// - z: pointer to z position to load value into +// - rx: pointer to rotation x to load value into +// - ry: pointer to rotation y to load value into +// Returns: +// - non-zero if the state entry was successfully found and loaded int db_load_state(float *x, float *y, float *z, float *rx, float *ry) { if (!db_enabled) { return 0; @@ -312,6 +359,14 @@ int db_load_state(float *x, float *y, float *z, float *rx, float *ry) { return result; } +// Let one of the workers insert a block into the database. +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - w: block id void db_insert_block(int p, int q, int x, int y, int z, int w) { if (!db_enabled) { return; @@ -322,6 +377,14 @@ void db_insert_block(int p, int q, int x, int y, int z, int w) { mtx_unlock(&mtx); } +// Actually insert a block into the database. +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - w: block id value void _db_insert_block(int p, int q, int x, int y, int z, int w) { sqlite3_reset(insert_block_stmt); sqlite3_bind_int(insert_block_stmt, 1, p); @@ -333,6 +396,14 @@ void _db_insert_block(int p, int q, int x, int y, int z, int w) { sqlite3_step(insert_block_stmt); } +// Let one of the workers insert a light into the database. +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - w: light value void db_insert_light(int p, int q, int x, int y, int z, int w) { if (!db_enabled) { return; @@ -343,6 +414,14 @@ void db_insert_light(int p, int q, int x, int y, int z, int w) { mtx_unlock(&mtx); } +// Actually insert a light into the database. +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - w: light value void _db_insert_light(int p, int q, int x, int y, int z, int w) { sqlite3_reset(insert_light_stmt); sqlite3_bind_int(insert_light_stmt, 1, p); @@ -354,6 +433,15 @@ void _db_insert_light(int p, int q, int x, int y, int z, int w) { sqlite3_step(insert_light_stmt); } +// Insert a sign on the given block and face from the database +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - face: which face of the block the sign is to be on +// - text: the sign's text content void db_insert_sign( int p, int q, int x, int y, int z, int face, const char *text) { @@ -371,6 +459,12 @@ void db_insert_sign( sqlite3_step(insert_sign_stmt); } +// Delete a sign on the given block and face from the database +// Arguments: +// - x: block x position +// - y: block y position +// - z: block z position +// - face: which face of the block the sign to delete is on void db_delete_sign(int x, int y, int z, int face) { if (!db_enabled) { return; @@ -383,6 +477,11 @@ void db_delete_sign(int x, int y, int z, int face) { sqlite3_step(delete_sign_stmt); } +// Delete signs on given block from the database +// Arguments: +// - x: block x position +// - y: block y position +// - z: block z position void db_delete_signs(int x, int y, int z) { if (!db_enabled) { return; @@ -394,6 +493,9 @@ void db_delete_signs(int x, int y, int z) { sqlite3_step(delete_signs_stmt); } +// Delete all signs from the database +// Arguments: none +// Returns: none void db_delete_all_signs() { if (!db_enabled) { return; @@ -401,6 +503,12 @@ void db_delete_all_signs() { sqlite3_exec(db, "delete from sign;", NULL, NULL, NULL); } +// Load all of the blocks from database in chunk +// Arguments: +// - map: block map destination to load block values into +// - p: chunk x position +// - q: chunk z position +// Returns: none void db_load_blocks(Map *map, int p, int q) { if (!db_enabled) { return; @@ -419,6 +527,13 @@ void db_load_blocks(Map *map, int p, int q) { mtx_unlock(&load_mtx); } +// Load all of the lights from database in chunk +// Arguments: +// - map: light map pointer to output light values into +// - p: chunk x position +// - q: chunk z position +// Returns: +// - modifies lights in given map pointer void db_load_lights(Map *map, int p, int q) { if (!db_enabled) { return; @@ -437,6 +552,13 @@ void db_load_lights(Map *map, int p, int q) { mtx_unlock(&load_mtx); } +// Load all of the signs from database in chunk +// Arguments: +// - list: pointer to output list of signs into +// - p: chunk x position +// - q: chunk z position +// Returns: +// - adds sign entries to list void db_load_signs(SignList *list, int p, int q) { if (!db_enabled) { return; @@ -455,6 +577,12 @@ void db_load_signs(SignList *list, int p, int q) { } } +// Get the key value for the chunk at the given position +// Arguments: +// - p: chunk x position +// - q: chunk z position +// Returns: +// - key value int db_get_key(int p, int q) { if (!db_enabled) { return 0; @@ -468,6 +596,12 @@ int db_get_key(int p, int q) { return 0; } +// Let one of the workers set a key for a chunk +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - key: ket to be set for the chunk +// Returns: none void db_set_key(int p, int q, int key) { if (!db_enabled) { return; @@ -478,6 +612,12 @@ void db_set_key(int p, int q, int key) { mtx_unlock(&mtx); } +// Actually set a key for a chunk +// Arguments: +// - p: chunk x position +// - q: chunk z position +// - key: ket to be set for the chunk +// Returns: none void _db_set_key(int p, int q, int key) { sqlite3_reset(set_key_stmt); sqlite3_bind_int(set_key_stmt, 1, p); @@ -486,6 +626,10 @@ void _db_set_key(int p, int q, int key) { sqlite3_step(set_key_stmt); } +// Start a worker with the database +// Arguments: +// - path: argument to pass to the worker +// Returns: none void db_worker_start(char *path) { if (!db_enabled) { return; @@ -497,6 +641,9 @@ void db_worker_start(char *path) { thrd_create(&thrd, db_worker_run, path); } +// Stop workers from using the database. +// Arguments: none +// Returns: none void db_worker_stop() { if (!db_enabled) { return; @@ -512,6 +659,11 @@ void db_worker_stop() { ring_free(&ring); } +// This is where a worker will fetch and perform database operations. +// Arguments: +// - arg: unused in this function +// Returns: +// - 0 int db_worker_run(void *arg) { int running = 1; while (running) { @@ -541,3 +693,4 @@ int db_worker_run(void *arg) { } return 0; } + diff --git a/src/item.c b/src/item.c index 907c4c30b..ce761e6b4 100644 --- a/src/item.c +++ b/src/item.c @@ -1,8 +1,9 @@ #include "item.h" #include "util.h" +// The list of block ids the player can build. +// Note: the player cannot build every block type (such as clouds). const int items[] = { - // items the user can build GRASS, SAND, STONE, @@ -62,7 +63,7 @@ const int items[] = { const int item_count = sizeof(items) / sizeof(int); const int blocks[256][6] = { - // w => (left, right, top, bottom, front, back) tiles + // maps w (block id) => (left, right, top, bottom, front, back) tiles {0, 0, 0, 0, 0, 0}, // 0 - empty {16, 16, 32, 0, 16, 16}, // 1 - grass {1, 1, 1, 1, 1, 1}, // 2 - sand @@ -130,7 +131,7 @@ const int blocks[256][6] = { }; const int plants[256] = { - // w => tile + // w (block id) => tile 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 16 48, // 17 - tall grass 49, // 18 - yellow flower @@ -141,6 +142,11 @@ const int plants[256] = { 54, // 23 - blue flower }; +// Predicate function for whether a block id is a plant type +// Arguments: +// - w: block id (block type) +// Returns: +// - boolean whether block type is a plant int is_plant(int w) { switch (w) { case TALL_GRASS: @@ -156,6 +162,11 @@ int is_plant(int w) { } } +// Predicate function for whether a block id is an obstacle (blocking movement) +// Arguments: +// - w: block id (block type) +// Returns: +// - boolean whether block type is obstacle int is_obstacle(int w) { w = ABS(w); if (is_plant(w)) { @@ -170,6 +181,11 @@ int is_obstacle(int w) { } } +// Predicate function for whether a block id is transparent +// Arguments: +// - w: block id (block type) +// Returns: +// - boolean whether block type is transparent int is_transparent(int w) { if (w == EMPTY) { return 1; @@ -188,6 +204,11 @@ int is_transparent(int w) { } } +// Predicate function for whether a block id is destructable +// Arguments: +// - w: block id (block type) +// Returns: +// - boolean whether block type is destructable int is_destructable(int w) { switch (w) { case EMPTY: @@ -197,3 +218,4 @@ int is_destructable(int w) { return 1; } } + diff --git a/src/item.h b/src/item.h index ed801cf15..22cd299c7 100644 --- a/src/item.h +++ b/src/item.h @@ -1,6 +1,7 @@ #ifndef _item_h_ #define _item_h_ +// Block ids/types #define EMPTY 0 #define GRASS 1 #define SAND 2 diff --git a/src/main.c b/src/main.c index a0b622861..704a19514 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,19 @@ #define WORKER_BUSY 1 #define WORKER_DONE 2 +// World chunk data (big area of blocks) +// - map: block hash map +// - lights: light hash map +// - signs: signs in this chunk +// - p: chunk x coordinate +// - q: chunk z coordinate +// - faces: number of faces +// - sign_faces: number of sign face +// - dirty: dirty flag +// - miny: minimum Y value held by any block +// - maxy: maximum Y value held by any block +// - buffer: +// - sign_buffer: typedef struct { Map map; Map lights; @@ -54,6 +67,16 @@ typedef struct { GLuint sign_buffer; } Chunk; +// A single item that a Worker can work on +// - p: chunk x coordinate +// - q: chunk y coordinate +// - load +// - block_maps +// - light_maps +// - miny +// - maxy +// - faces +// - data typedef struct { int p; int q; @@ -66,6 +89,13 @@ typedef struct { GLfloat *data; } WorkerItem; +// Worker data (for multi-threading) +// - index: +// - state: +// - thrd: thread +// - mtx: mutex +// - cnd: condition variable +// - item: typedef struct { int index; int state; @@ -75,6 +105,11 @@ typedef struct { WorkerItem item; } Worker; +// Block data +// - x: x position +// - y: y position +// - z: z position +// - w: block id / value typedef struct { int x; int y; @@ -82,15 +117,29 @@ typedef struct { int w; } Block; +// State for a player +// - x: x position (player feet level) +// - y: y position (player feet level) +// - z: z position (player feet level) +// - rx: rotation x +// - ry: rotation y +// - t: keep track of time, for interpolation typedef struct { - float x; + float x; float y; float z; float rx; float ry; - float t; + float t; } State; +// Player +// - id +// - name: name string buffer +// - state: current player position state +// - state1: another state, for interpolation +// - state2: another state, for interpolation +// - buffer: some GL buffer id (?) typedef struct { int id; char name[MAX_NAME_LENGTH]; @@ -100,6 +149,19 @@ typedef struct { GLuint buffer; } Player; +// OpenGL attribute data +// - program: +// - position: +// - normal: +// - uv: +// - matrix: +// - sampler: +// - camera: +// - timer: +// - extra1: +// - extra2: +// - extra3: +// - extra4: typedef struct { GLuint program; GLuint position; @@ -115,6 +177,42 @@ typedef struct { GLuint extra4; } Attrib; +// Program state model +// - window: +// - workers: +// - chunks: +// - chunk_count: +// - create_radius: +// - render_radius: +// - delete_radius: +// - sign_radius: +// - players: +// - player_count: +// - typing: +// - typing_buffer: +// - message_index: +// - messages: +// - width: +// - height: +// - observe1: +// - observe2: +// - flying: +// - item_index: current selected block to place next +// - scale: +// - ortho: +// - fov: +// - suppress_char: +// - mode: +// - mode_changed: +// - db_path: +// - server_addr: +// - server_port: +// - day_length: +// - time_changed: +// - block0: +// - block1: +// - copy0: +// - copy1: typedef struct { GLFWwindow *window; Worker workers[WORKERS]; @@ -153,13 +251,24 @@ typedef struct { Block copy1; } Model; +// The main game state is kept here static Model model; static Model *g = &model; +// Convert a value in block space to chunk space +// Arguments: +// - x: block coordinate +// Returns: +// - chunk coordinate the block coordinate is in int chunked(float x) { return floorf(roundf(x) / CHUNK_SIZE); } +// Get the current time of day +// Depends on glfwGetTime() +// Arguments: none +// Returns: +// - time value between 0.0 and 1.0 float time_of_day() { if (g->day_length <= 0) { return 0.5; @@ -171,6 +280,9 @@ float time_of_day() { return t; } +// Arguments: none +// Returns: +// - daylight value float get_daylight() { float timer = time_of_day(); if (timer < 0.5) { @@ -183,6 +295,10 @@ float get_daylight() { } } +// Note: depends on window size and frame buffer size +// Arguments: none +// Returns: +// - scale factor int get_scale_factor() { int window_width, window_height; int buffer_width, buffer_height; @@ -194,6 +310,13 @@ int get_scale_factor() { return result; } +// Arguments: +// - rx: rotation x +// - ry: rotation y +// Returns: +// - vx: vector x +// - vy: vector y +// - vz: vector z void get_sight_vector(float rx, float ry, float *vx, float *vy, float *vz) { float m = cosf(ry); *vx = cosf(rx - RADIANS(90)) * m; @@ -201,6 +324,17 @@ void get_sight_vector(float rx, float ry, float *vx, float *vy, float *vz) { *vz = sinf(rx - RADIANS(90)) * m; } +// Get the motion vector for a player's state +// Arguments: +// - flying: whether the player is flying +// - sz: strafe z +// - sx: strafe x +// - rx: rotation x +// - ry: rotation y +// Returns: +// - vx: vector x +// - vy: vector y +// - vz: vector z void get_motion_vector(int flying, int sz, int sx, float rx, float ry, float *vx, float *vy, float *vz) { *vx = 0; *vy = 0; *vz = 0; @@ -209,6 +343,7 @@ void get_motion_vector(int flying, int sz, int sx, float rx, float ry, } float strafe = atan2f(sz, sx); if (flying) { + // Flying motion float m = cosf(ry); float y = sinf(ry); if (sx) { @@ -225,12 +360,17 @@ void get_motion_vector(int flying, int sz, int sx, float rx, float ry, *vz = sinf(rx + strafe) * m; } else { + // Walking motion *vx = cosf(rx + strafe); *vy = 0; *vz = sinf(rx + strafe); } } +// Generate the position buffer for the crosshairs in the middle of the screen. +// Arguments: none +// Returns: +// - OpenGL buffer handle GLuint gen_crosshair_buffer() { int x = g->width / 2; int y = g->height / 2; @@ -242,19 +382,42 @@ GLuint gen_crosshair_buffer() { return gen_buffer(sizeof(data), data); } +// Create a new cube wireframe buffer +// Arguments: +// - x: cube x position +// - y: cube y position +// - z: cube z position +// - n: cube scale, distance from center to faces +// Returns: +// - OpenGL buffer handle GLuint gen_wireframe_buffer(float x, float y, float z, float n) { float data[72]; make_cube_wireframe(data, x, y, z, n); return gen_buffer(sizeof(data), data); } +// Create the sky buffer (sphere shape) +// Arguments: none +// Returns: OpenGL buffer handle GLuint gen_sky_buffer() { + // The size of this data array should match the detail parameter in make_sphere() float data[12288]; make_sphere(data, 1, 3); return gen_buffer(sizeof(data), data); } +// Create a new cube buffer +// Arguments: +// - x: cube x position +// - y: cube y position +// - z: cube z position +// - n: cube scale, distance from center to faces +// - w: block id for textures +// Returns: +// - OpenGL buffer handle GLuint gen_cube_buffer(float x, float y, float z, float n, int w) { + // Each face has 10 component float properties. + // A cube model has 6 faces GLfloat *data = malloc_faces(10, 6); float ao[6][4] = {0}; float light[6][4] = { @@ -269,7 +432,18 @@ GLuint gen_cube_buffer(float x, float y, float z, float n, int w) { return gen_faces(10, 6, data); } +// Generate a buffer for a plant block model at a given location +// Arguments: +// - x: block x position +// - y: block y position +// - z: block z position +// - n: scale, distance from center to rectangle edge +// - w: plant block type +// Returns: +// - OpenGL buffer handle GLuint gen_plant_buffer(float x, float y, float z, float n, int w) { + // Each face has 10 component float properties. + // A plant model has 4 faces because there are 2 squares each with 2 sides GLfloat *data = malloc_faces(10, 4); float ao = 0; float light = 1; @@ -277,22 +451,49 @@ GLuint gen_plant_buffer(float x, float y, float z, float n, int w) { return gen_faces(10, 4, data); } +// Create buffer for player model +// Arguments: +// - x: player x position +// - y: player y position +// - z: player z position +// - rx: player rotation x +// - ry: player rotation y +// Returns: +// - OpenGL buffer handle GLuint gen_player_buffer(float x, float y, float z, float rx, float ry) { + // Player model is just a cube + // Each face has 10 component float properties. + // A cube model has 6 faces GLfloat *data = malloc_faces(10, 6); make_player(data, x, y, z, rx, ry); return gen_faces(10, 6, data); } +// Create a 2D screen model for a text string +// Arguments: +// - x: screen x +// - y: screen y +// - n: scale +// - text: text data to be displayed +// Returns: +// - OpenGL buffer handle GLuint gen_text_buffer(float x, float y, float n, char *text) { int length = strlen(text); GLfloat *data = malloc_faces(4, length); for (int i = 0; i < length; i++) { + // Multiply by 24 because there are 24 properties per character make_character(data + i * 24, x, y, n / 2, n, text[i]); x += n; } return gen_faces(4, length, data); } +// Draws 3D triangle models +// Arguments: +// - attrib: attributes to be used for rendering the triangles +// - buffer: triangles data +// - count: number of triangles +// Returns: none void draw_triangles_3d_ao(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(attrib->position); @@ -311,6 +512,12 @@ void draw_triangles_3d_ao(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, 0); } +// Draw triangles for 3D text +// Arguments: +// - attrib: attributes to be used for rendering +// - buffer +// - count: number of triangles +// Returns: none void draw_triangles_3d_text(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(attrib->position); @@ -325,6 +532,12 @@ void draw_triangles_3d_text(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, 0); } +// Draw 3D (textured) triangle models +// Arguments: +// - attrib: attributes to be used for rendering +// - buffer +// - count +// Returns: none void draw_triangles_3d(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(attrib->position); @@ -343,6 +556,12 @@ void draw_triangles_3d(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, 0); } +// Draw 2D (textured) triangle models +// Arguments: +// - attrib: attributes to be used for rendering +// - buffer +// - count: number of triangles +// Returns: none void draw_triangles_2d(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(attrib->position); @@ -357,6 +576,13 @@ void draw_triangles_2d(Attrib *attrib, GLuint buffer, int count) { glBindBuffer(GL_ARRAY_BUFFER, 0); } +// Draw lines +// Arguments: +// - attrib: attributes to be used for rendering +// - buffer +// - components +// - count +// Returns: none void draw_lines(Attrib *attrib, GLuint buffer, int components, int count) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(attrib->position); @@ -367,14 +593,23 @@ void draw_lines(Attrib *attrib, GLuint buffer, int components, int count) { glBindBuffer(GL_ARRAY_BUFFER, 0); } +// Draw a game chunk (of blocks) +// Arguments: +// Returns: none void draw_chunk(Attrib *attrib, Chunk *chunk) { draw_triangles_3d_ao(attrib, chunk->buffer, chunk->faces * 6); } +// Draw a block (item), which can be a plant shape or a cube shape +// Arguments: +// Returns: none void draw_item(Attrib *attrib, GLuint buffer, int count) { draw_triangles_3d_ao(attrib, buffer, count); } +// Draw 2D text +// Arguments: +// Returns: none void draw_text(Attrib *attrib, GLuint buffer, int length) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -382,6 +617,9 @@ void draw_text(Attrib *attrib, GLuint buffer, int length) { glDisable(GL_BLEND); } +// Draw the signs in a given chunk +// Arguments: +// Returns: none void draw_signs(Attrib *attrib, Chunk *chunk) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-8, -1024); @@ -389,6 +627,9 @@ void draw_signs(Attrib *attrib, Chunk *chunk) { glDisable(GL_POLYGON_OFFSET_FILL); } +// Draw a single sign model +// Arguments: +// Returns: none void draw_sign(Attrib *attrib, GLuint buffer, int length) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-8, -1024); @@ -396,18 +637,34 @@ void draw_sign(Attrib *attrib, GLuint buffer, int length) { glDisable(GL_POLYGON_OFFSET_FILL); } +// Draw a cube block model +// Arguments: +// Returns: none void draw_cube(Attrib *attrib, GLuint buffer) { draw_item(attrib, buffer, 36); } +// Draw a plant block model +// Arguments: +// Returns: none void draw_plant(Attrib *attrib, GLuint buffer) { draw_item(attrib, buffer, 24); } +// Draw a player model +// Arguments: +// - attrib: attributes to be used for rendering +// - player +// Returns: none void draw_player(Attrib *attrib, Player *player) { draw_cube(attrib, player->buffer); } +// Find a player with a certain id +// Arguments: +// - id: player id of the player to find +// Returns: +// - pointer to the player in the game model Player *find_player(int id) { for (int i = 0; i < g->player_count; i++) { Player *player = g->players + i; @@ -418,6 +675,15 @@ Player *find_player(int id) { return 0; } +// Update a player with a new position and rotation. +// Arguments: +// - player: player to modify +// - x: new position x +// - y: new position y +// - z: new position z +// - rx: new rotation x +// - ry: new rotation y +// - interpolate: whether to interpolate position void update_player(Player *player, float x, float y, float z, float rx, float ry, int interpolate) { @@ -442,6 +708,9 @@ void update_player(Player *player, } } +// Arguments: +// - player +// Returns: none void interpolate_player(Player *player) { State *s1 = &player->state1; State *s2 = &player->state2; @@ -460,6 +729,10 @@ void interpolate_player(Player *player) { 0); } +// Look for a player with the given id and remove that player's data +// Arguments: +// - id: id of the player to delete +// Returns: none void delete_player(int id) { Player *player = find_player(id); if (!player) { @@ -472,6 +745,9 @@ void delete_player(int id) { g->player_count = count; } +// Delete all of the current players +// Arguments: none +// Returns: none void delete_all_players() { for (int i = 0; i < g->player_count; i++) { Player *player = g->players + i; @@ -480,15 +756,28 @@ void delete_all_players() { g->player_count = 0; } +// Get the distance between 2 players. +// Arguments: +// - p1: a player +// - p2: another player +// Returns: +// - distance float player_player_distance(Player *p1, Player *p2) { State *s1 = &p1->state; State *s2 = &p2->state; + // Calculate magnitude of the vector from p2 position to p1 position float x = s2->x - s1->x; float y = s2->y - s1->y; float z = s2->z - s1->z; return sqrtf(x * x + y * y + z * z); } +// Get the distance between where p1 is looking and p2's position. +// Arguments: +// - p1 +// - p2 +// Returns: +// - distance float player_crosshair_distance(Player *p1, Player *p2) { State *s1 = &p1->state; State *s2 = &p2->state; @@ -504,6 +793,12 @@ float player_crosshair_distance(Player *p1, Player *p2) { return sqrtf(x * x + y * y + z * z); } +// Find the player that the given player is looking at. +// Arguments: +// - player: the given player +// Returns: +// - the closest player that is in range and that the given player is looking +// near. Player *player_crosshair(Player *player) { Player *result = 0; float threshold = RADIANS(5); @@ -525,6 +820,12 @@ Player *player_crosshair(Player *player) { return result; } +// Try to find a chunk with at specific chunk coordinates +// Arguments: +// - p: chunk x +// - q: chunk z +// Returns: +// - chunk pointer Chunk *find_chunk(int p, int q) { for (int i = 0; i < g->chunk_count; i++) { Chunk *chunk = g->chunks + i; @@ -535,12 +836,30 @@ Chunk *find_chunk(int p, int q) { return 0; } +// Get the "distance" of a chunk from the given chunk coordinates. +// Arguments: +// - chunk: chunk to test distance the distance from (p, q) +// - p: chunk x +// - q: chunk z +// Returns: +// - distance in chunk space, where "distance" means the maximum value of the +// change in p and the change in q. int chunk_distance(Chunk *chunk, int p, int q) { int dp = ABS(chunk->p - p); int dq = ABS(chunk->q - q); return MAX(dp, dq); } +// Predicate function to determine if a chunk is visible within the given +// frustrum planes. +// Arguments: +// - planes: frustrum planes to check if the chunk is visible within +// - p: chunk x coordinate +// - q: chunk z coordinate +// - miny: minimum block y coordinate in the chunk +// - maxy: maximum block y coordinate in the chunk +// Returns: +// - non-zero if the chunk is visible int chunk_visible(float planes[6][4], int p, int q, int miny, int maxy) { int x = p * CHUNK_SIZE - 1; int z = q * CHUNK_SIZE - 1; @@ -582,6 +901,12 @@ int chunk_visible(float planes[6][4], int p, int q, int miny, int maxy) { return 1; } +// Find the highest y position of a block at a given (x,z) position. +// Arguments: +// - x +// - z +// Returns: +// - highest y value, or -1 if not found int highest_block(float x, float z) { int result = -1; int nx = roundf(x); @@ -600,6 +925,24 @@ int highest_block(float x, float z) { return result; } +// Finds the closest block in a map found by casting a hit ray. +// Meant to be called by hit_test(). +// Arguments: +// - map: map to search for hitting blocks in +// - max_distance +// - previous: boolean +// - x: ray start x +// - y: ray start y +// - z: ray start z +// - vx: ray x direction +// - vy: ray y direction +// - vz: ray z direction +// - hx: pointer to output hit x position +// - hy: pointer to output hit y position +// - hz: pointer to output hit z position +// Returns: +// - the block type that was hit, returns 0 if there was no block found +// - writes hit output to hx, hy, and hz int _hit_test( Map *map, float max_distance, int previous, float x, float y, float z, @@ -632,6 +975,19 @@ int _hit_test( return 0; } +// Finds the closest block found by casting a hit ray. +// Arguments: +// - previous: boolean +// - x: ray start x +// - y: ray start y +// - z: ray start z +// - rx: ray x direction +// - ry: ray y direction +// - bx: pointer to output hit x position +// - by: pointer to output hit y position +// - bz: pointer to output hit z position +// Returns: +// - the block type that was hit, returns 0 if no block was found int hit_test( int previous, float x, float y, float z, float rx, float ry, int *bx, int *by, int *bz) @@ -663,6 +1019,15 @@ int hit_test( return result; } +// See which block face a player is looking at +// Arguments: +// - player +// - x: output pointer to the x position of the hit block +// - y: output pointer to the y position of the hit block +// - z: output pointer to the z position of the hit block +// - face: output pointer to the face number of the hit block +// Returns: +// - non-zero if a face was hit int hit_test_face(Player *player, int *x, int *y, int *z, int *face) { State *s = &player->state; int w = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, x, y, z); @@ -696,8 +1061,19 @@ int hit_test_face(Player *player, int *x, int *y, int *z, int *face) { return 0; } +// Do block collision for a player. +// Arguments: +// - height: player height in blocks +// - x: pointer to current player x position of feet +// - y: pointer to current player y position of feet +// - z: pointer to current player z position of feet +// Returns: +// - non-zero if the player collided with a block +// - may modify the x, y, and z values int collide(int height, float *x, float *y, float *z) { + // Default result: no collision int result = 0; + // Find the block map that the position is in int p = chunked(*x); int q = chunked(*z); Chunk *chunk = find_chunk(p, q); @@ -705,6 +1081,7 @@ int collide(int height, float *x, float *y, float *z) { return result; } Map *map = &chunk->map; + // Get the nearest block position int nx = roundf(*x); int ny = roundf(*y); int nz = roundf(*z); @@ -712,6 +1089,7 @@ int collide(int height, float *x, float *y, float *z) { float py = *y - ny; float pz = *z - nz; float pad = 0.25; + // Check each block position within the player's height for (int dy = 0; dy < height; dy++) { if (px < -pad && is_obstacle(map_get(map, nx - 1, ny - dy, nz))) { *x = nx - pad; @@ -737,6 +1115,18 @@ int collide(int height, float *x, float *y, float *z) { return result; } +// Predicate function to return whether a player position instersects the given +// block position. +// Arguments: +// - height +// - x: player x position +// - y: player y position +// - z: player z position +// - hx: block x position to test +// - hy: block y position to test +// - hz: block z position to test +// Returns: +// - non-zero if the player intersects the given block position int player_intersects_block( int height, float x, float y, float z, @@ -753,14 +1143,24 @@ int player_intersects_block( return 0; } +// Generate the buffer data for a single sign model +// Arguments: +// - data: pointer to write the data to +// - x +// - y +// - z +// - face +// - text: sign ASCII text string (null-terminated) +// Returns: +// - number of character faces int _gen_sign_buffer( GLfloat *data, float x, float y, float z, int face, const char *text) { - static const int glyph_dx[8] = {0, 0, -1, 1, 1, 0, -1, 0}; - static const int glyph_dz[8] = {1, -1, 0, 0, 0, -1, 0, 1}; - static const int line_dx[8] = {0, 0, 0, 0, 0, 1, 0, -1}; - static const int line_dy[8] = {-1, -1, -1, -1, 0, 0, 0, 0}; - static const int line_dz[8] = {0, 0, 0, 0, 1, 0, -1, 0}; + static const int glyph_dx[8] = { 0, 0, -1, 1, 1, 0, -1, 0 }; + static const int glyph_dz[8] = { 1, -1, 0, 0, 0, -1, 0, 1 }; + static const int line_dx[8] = { 0, 0, 0, 0, 0, 1, 0, -1 }; + static const int line_dy[8] = {-1, -1, -1, -1, 0, 0, 0, 0 }; + static const int line_dz[8] = { 0, 0, 0, 0, 1, 0, -1, 0 }; if (face < 0 || face >= 8) { return 0; } @@ -816,6 +1216,10 @@ int _gen_sign_buffer( return count; } +// Create the game's sign buffer for a chunk +// Arguments: +// - chunk: the chunk to generate the sign models for +// Returns: none void gen_sign_buffer(Chunk *chunk) { SignList *signs = &chunk->signs; @@ -840,6 +1244,11 @@ void gen_sign_buffer(Chunk *chunk) { chunk->sign_faces = faces; } +// Predicate function for whether a given chunk has any block light values +// Arguments: +// - chunk: the chunk to check +// Returns: +// - non-zero if the chunk has lights int has_lights(Chunk *chunk) { if (!SHOW_LIGHTS) { return 0; @@ -862,6 +1271,10 @@ int has_lights(Chunk *chunk) { return 0; } +// Mark a chunk as dirty and also potentially mark the neighboring chunks +// Arguments: +// - chunk: chunk to mark as dirty +// Returns: none void dirty_chunk(Chunk *chunk) { chunk->dirty = 1; if (has_lights(chunk)) { @@ -876,6 +1289,13 @@ void dirty_chunk(Chunk *chunk) { } } +// Arguments: +// - neighbors +// - lights +// - shades +// - ao +// - light +// Returns: none void occlusion( char neighbors[27], char lights[27], float shades[27], float ao[6][4], float light[6][4]) @@ -927,6 +1347,15 @@ void occlusion( #define XYZ(x, y, z) ((y) * XZ_SIZE * XZ_SIZE + (x) * XZ_SIZE + (z)) #define XZ(x, z) ((x) * XZ_SIZE + (z)) +// Arguments: +// - opaque +// - light +// - x +// - y +// - z +// - w +// - force +// Returns: none void light_fill( char *opaque, char *light, int x, int y, int z, int w, int force) @@ -955,6 +1384,9 @@ void light_fill( light_fill(opaque, light, x, y, z + 1, w, 0); } +// Arguments: +// - item +// Returns: none void compute_chunk(WorkerItem *item) { char *opaque = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char)); char *light = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char)); @@ -1133,6 +1565,10 @@ void compute_chunk(WorkerItem *item) { item->data = data; } +// Arguments: +// - chunk +// - item +// Returns: none void generate_chunk(Chunk *chunk, WorkerItem *item) { chunk->miny = item->miny; chunk->maxy = item->maxy; @@ -1142,6 +1578,9 @@ void generate_chunk(Chunk *chunk, WorkerItem *item) { gen_sign_buffer(chunk); } +// Arguments: +// - chunk +// Returns: none void gen_chunk_buffer(Chunk *chunk) { WorkerItem _item; WorkerItem *item = &_item; @@ -1168,11 +1607,24 @@ void gen_chunk_buffer(Chunk *chunk) { chunk->dirty = 0; } +// Callback function used for world generation +// Arguments: +// - x: x position +// - y: y position +// - z: z position +// - w: block type +// - arg: pointer to map (is void* so that world gen doesn't need to know the type) +// Returns: +// - no return value +// - modifies the Map pointed to by "arg" void map_set_func(int x, int y, int z, int w, void *arg) { Map *map = (Map *)arg; map_set(map, x, y, z, w); } +// Arguments: +// - item +// Returns: none void load_chunk(WorkerItem *item) { int p = item->p; int q = item->q; @@ -1183,11 +1635,20 @@ void load_chunk(WorkerItem *item) { db_load_lights(light_map, p, q); } +// Arguments: +// - p +// - q +// Returns: none void request_chunk(int p, int q) { int key = db_get_key(p, q); client_chunk(p, q, key); } +// Arguments: +// - chunk +// - p +// - q +// Returns: none void init_chunk(Chunk *chunk, int p, int q) { chunk->p = p; chunk->q = q; @@ -1208,6 +1669,11 @@ void init_chunk(Chunk *chunk, int p, int q) { map_alloc(light_map, dx, dy, dz, 0xf); } +// Arguments: +// - chunk +// - p +// - q +// Returns: none void create_chunk(Chunk *chunk, int p, int q) { init_chunk(chunk, p, q); @@ -1222,6 +1688,9 @@ void create_chunk(Chunk *chunk, int p, int q) { request_chunk(p, q); } +// Delete the chunks that should be deleted (because they are out of range) +// Arguments: none +// Returns: none void delete_chunks() { int count = g->chunk_count; State *s1 = &g->players->state; @@ -1231,6 +1700,8 @@ void delete_chunks() { for (int i = 0; i < count; i++) { Chunk *chunk = g->chunks + i; int delete = 1; + // Do not delete a chunk if the chunk is in range + // for any of the player's position states. for (int j = 0; j < 3; j++) { State *s = states[j]; int p = chunked(s->x); @@ -1253,6 +1724,9 @@ void delete_chunks() { g->chunk_count = count; } +// Delete all chunk data +// Arguments: none +// Returns: none void delete_all_chunks() { for (int i = 0; i < g->chunk_count; i++) { Chunk *chunk = g->chunks + i; @@ -1265,6 +1739,8 @@ void delete_all_chunks() { g->chunk_count = 0; } +// Arguments: none +// Returns: none void check_workers() { for (int i = 0; i < WORKERS; i++) { Worker *worker = g->workers + i; @@ -1304,6 +1780,10 @@ void check_workers() { } } +// Force the chunks around the given player to generate on this main thread. +// Arguments: +// - player: player to generate chunks for +// Returns: none void force_chunks(Player *player) { State *s = &player->state; int p = chunked(s->x); @@ -1328,6 +1808,10 @@ void force_chunks(Player *player) { } } +// Arguments: +// - player +// - worker +// Returns: none void ensure_chunks_worker(Player *player, Worker *worker) { State *s = &player->state; float matrix[16]; @@ -1415,6 +1899,9 @@ void ensure_chunks_worker(Player *player, Worker *worker) { cnd_signal(&worker->cnd); } +// Arguments: +// - player +// Returns: none void ensure_chunks(Player *player) { check_workers(); force_chunks(player); @@ -1428,6 +1915,10 @@ void ensure_chunks(Player *player) { } } +// Arguments: +// - arg +// Returns: +// - integer int worker_run(void *arg) { Worker *worker = (Worker *)arg; int running = 1; @@ -1449,6 +1940,11 @@ int worker_run(void *arg) { return 0; } +// Arguments: +// - x +// - y +// - z +// Returns: none void unset_sign(int x, int y, int z) { int p = chunked(x); int q = chunked(z); @@ -1465,6 +1961,12 @@ void unset_sign(int x, int y, int z) { } } +// Arguments: +// - x +// - y +// - z +// - face +// Returns: none void unset_sign_face(int x, int y, int z, int face) { int p = chunked(x); int q = chunked(z); @@ -1481,6 +1983,16 @@ void unset_sign_face(int x, int y, int z, int face) { } } +// Arguments: +// - p +// - q +// - x +// - y +// - z +// - face +// - text +// - dirty +// Returns: none void _set_sign( int p, int q, int x, int y, int z, int face, const char *text, int dirty) { @@ -1499,6 +2011,13 @@ void _set_sign( db_insert_sign(p, q, x, y, z, face, text); } +// Arguments: +// - x +// - y +// - z +// - face +// - text +// Returns: none void set_sign(int x, int y, int z, int face, const char *text) { int p = chunked(x); int q = chunked(z); @@ -1506,6 +2025,11 @@ void set_sign(int x, int y, int z, int face, const char *text) { client_sign(x, y, z, face, text); } +// Arguments: +// - x +// - y +// - z +// Returns: none void toggle_light(int x, int y, int z) { int p = chunked(x); int q = chunked(z); @@ -1520,6 +2044,14 @@ void toggle_light(int x, int y, int z) { } } +// Arguments: +// - p +// - q +// - x +// - y +// - z +// - w +// Returns: none void set_light(int p, int q, int x, int y, int z, int w) { Chunk *chunk = find_chunk(p, q); if (chunk) { @@ -1534,6 +2066,15 @@ void set_light(int p, int q, int x, int y, int z, int w) { } } +// Arguments: +// - p +// - q +// - x +// - y +// - z +// - w +// - dirty +// Returns: none void _set_block(int p, int q, int x, int y, int z, int w, int dirty) { Chunk *chunk = find_chunk(p, q); if (chunk) { @@ -1548,12 +2089,19 @@ void _set_block(int p, int q, int x, int y, int z, int w, int dirty) { else { db_insert_block(p, q, x, y, z, w); } + // If a block is removed, then remove any signs and light source from that block. if (w == 0 && chunked(x) == p && chunked(z) == q) { unset_sign(x, y, z); set_light(p, q, x, y, z, 0); } } +// Arguments: +// - x +// - y +// - z +// - w +// Returns: none void set_block(int x, int y, int z, int w) { int p = chunked(x); int q = chunked(z); @@ -1575,6 +2123,12 @@ void set_block(int x, int y, int z, int w) { client_block(x, y, z, w); } +// Arguments: +// - x +// - y +// - z +// - w +// Returns: none void record_block(int x, int y, int z, int w) { memcpy(&g->block1, &g->block0, sizeof(Block)); g->block0.x = x; @@ -1583,6 +2137,12 @@ void record_block(int x, int y, int z, int w) { g->block0.w = w; } +// Arguments: +// - x +// - y +// - z +// Returns: +// - w int get_block(int x, int y, int z) { int p = chunked(x); int q = chunked(z); @@ -1594,6 +2154,12 @@ int get_block(int x, int y, int z) { return 0; } +// Arguments: +// - x +// - y +// - z +// - w +// Returns: none void builder_block(int x, int y, int z, int w) { if (y <= 0 || y >= 256) { return; @@ -1606,6 +2172,11 @@ void builder_block(int x, int y, int z, int w) { } } +// Arguments: +// - attrib +// - player +// Returns: +// - number of faces int render_chunks(Attrib *attrib, Player *player) { int result = 0; State *s = &player->state; @@ -1644,6 +2215,10 @@ int render_chunks(Attrib *attrib, Player *player) { return result; } +// Arguments: +// - attrib +// - player +// Returns: none void render_signs(Attrib *attrib, Player *player) { State *s = &player->state; int p = chunked(s->x); @@ -1672,6 +2247,10 @@ void render_signs(Attrib *attrib, Player *player) { } } +// Arguments: +// - attrib +// - player +// Returns: none void render_sign(Attrib *attrib, Player *player) { if (!g->typing || g->typing_buffer[0] != CRAFT_KEY_SIGN) { return; @@ -1699,6 +2278,10 @@ void render_sign(Attrib *attrib, Player *player) { del_buffer(buffer); } +// Arguments: +// - attrib +// - player +// Returns: none void render_players(Attrib *attrib, Player *player) { State *s = &player->state; float matrix[16]; @@ -1718,6 +2301,11 @@ void render_players(Attrib *attrib, Player *player) { } } +// Arguments: +// - attrib +// - player +// - buffer +// Returns: none void render_sky(Attrib *attrib, Player *player, GLuint buffer) { State *s = &player->state; float matrix[16]; @@ -1731,6 +2319,10 @@ void render_sky(Attrib *attrib, Player *player, GLuint buffer) { draw_triangles_3d(attrib, buffer, 512 * 3); } +// Arguments: +// - attrib +// - player +// Returns: none void render_wireframe(Attrib *attrib, Player *player) { State *s = &player->state; float matrix[16]; @@ -1751,6 +2343,9 @@ void render_wireframe(Attrib *attrib, Player *player) { } } +// Arguments: +// - attrib +// Returns: none void render_crosshairs(Attrib *attrib) { float matrix[16]; set_matrix_2d(matrix, g->width, g->height); @@ -1764,6 +2359,9 @@ void render_crosshairs(Attrib *attrib) { glDisable(GL_COLOR_LOGIC_OP); } +// Arguments: +// - attrib +// Returns: none void render_item(Attrib *attrib) { float matrix[16]; set_matrix_item(matrix, g->width, g->height, g->scale); @@ -1785,6 +2383,14 @@ void render_item(Attrib *attrib) { } } +// Arguments: +// - attrib +// - justify +// - x +// - y +// - n +// - text +// Returns: none void render_text( Attrib *attrib, int justify, float x, float y, float n, char *text) { @@ -1801,6 +2407,9 @@ void render_text( del_buffer(buffer); } +// Arguments: +// - text +// Returns: none void add_message(const char *text) { printf("%s\n", text); snprintf( @@ -1808,6 +2417,8 @@ void add_message(const char *text) { g->message_index = (g->message_index + 1) % MAX_MESSAGES; } +// Arguments: none +// Returns: void login() { char username[128] = {0}; char identity_token[128] = {0}; @@ -1831,11 +2442,13 @@ void login() { } } +// Player copies block void copy() { memcpy(&g->copy0, &g->block0, sizeof(Block)); memcpy(&g->copy1, &g->block1, sizeof(Block)); } +// Player pastes structure void paste() { Block *c1 = &g->copy1; Block *c2 = &g->copy0; @@ -1858,6 +2471,14 @@ void paste() { } } +// (Used as a chat command). +// Arguments: +// - b1 +// - b2 +// - xc +// - yc +// - zc +// Returns: none void array(Block *b1, Block *b2, int xc, int yc, int zc) { if (b1->w != b2->w) { return; @@ -1881,6 +2502,12 @@ void array(Block *b1, Block *b2, int xc, int yc, int zc) { } } +// Place a cube made out of blocks (Used as a chat command). +// Arguments: +// - b1 +// - b2 +// - fill +// Returns: none void cube(Block *b1, Block *b2, int fill) { if (b1->w != b2->w) { return; @@ -1911,6 +2538,15 @@ void cube(Block *b1, Block *b2, int fill) { } } +// Place a sphere at a block location. (Used as a chat command) +// Arguments: +// - center: a block to take the center block position from +// - radius: sphere radius +// - fill +// - fx +// - fy +// - fz +// Returns: none void sphere(Block *center, int radius, int fill, int fx, int fy, int fz) { static const float offsets[8][3] = { {-0.5, -0.5, -0.5}, @@ -1960,6 +2596,13 @@ void sphere(Block *center, int radius, int fill, int fx, int fy, int fz) { } } +// Place a cylinder made out of blocks (Used as a chat command). +// Arguments: +// - b1 +// - b2 +// - radius +// - fill +// Returns: none void cylinder(Block *b1, Block *b2, int radius, int fill) { if (b1->w != b2->w) { return; @@ -1998,6 +2641,10 @@ void cylinder(Block *b1, Block *b2, int radius, int fill) { } } +// Place a tree at a block location. (Used as a chat command) +// Arguments: +// - block: a block to take the base block position from +// Returns: none void tree(Block *block) { int bx = block->x; int by = block->y; @@ -2018,10 +2665,16 @@ void tree(Block *block) { } } +// Parse a player chat command +// Arguments: +// - buffer: command string buffer to parse +// - forward: whether to forward the command as a chat message if the command is invalid +// Returns: none void parse_command(const char *buffer, int forward) { char username[128] = {0}; char token[128] = {0}; char server_addr[MAX_ADDR_LENGTH]; + // Note: DEFAULT_PORT is defined in "client.h" int server_port = DEFAULT_PORT; char filename[MAX_PATH_LENGTH]; int radius, count, xc, yc, zc; @@ -2063,6 +2716,7 @@ void parse_command(const char *buffer, int forward) { snprintf(g->db_path, MAX_PATH_LENGTH, "%s", DB_PATH); } else if (sscanf(buffer, "/view %d", &radius) == 1) { + // Set view radius if (radius >= 1 && radius <= 24) { g->create_radius = radius; g->render_radius = radius; @@ -2079,6 +2733,7 @@ void parse_command(const char *buffer, int forward) { paste(); } else if (strcmp(buffer, "/tree") == 0) { + // Place tree tree(&g->block0); } else if (sscanf(buffer, "/array %d %d %d", &xc, &yc, &zc) == 3) { @@ -2088,46 +2743,61 @@ void parse_command(const char *buffer, int forward) { array(&g->block1, &g->block0, count, count, count); } else if (strcmp(buffer, "/fcube") == 0) { + // Place a filled cube cube(&g->block0, &g->block1, 1); } else if (strcmp(buffer, "/cube") == 0) { + // Place an unfilled cube cube(&g->block0, &g->block1, 0); } else if (sscanf(buffer, "/fsphere %d", &radius) == 1) { + // Place a filled sphere with a radius sphere(&g->block0, radius, 1, 0, 0, 0); } else if (sscanf(buffer, "/sphere %d", &radius) == 1) { + // Place an unfilled sphere with a radius sphere(&g->block0, radius, 0, 0, 0, 0); } else if (sscanf(buffer, "/fcirclex %d", &radius) == 1) { + // Place a filled circle on the x-axis with a radius sphere(&g->block0, radius, 1, 1, 0, 0); } else if (sscanf(buffer, "/circlex %d", &radius) == 1) { + // Place an unfilled circle on the x-axis with a radius sphere(&g->block0, radius, 0, 1, 0, 0); } else if (sscanf(buffer, "/fcircley %d", &radius) == 1) { + // Place a filled circle on the y-axis with a radius sphere(&g->block0, radius, 1, 0, 1, 0); } else if (sscanf(buffer, "/circley %d", &radius) == 1) { + // Place an unfilled circle on the y-axis with a radius sphere(&g->block0, radius, 0, 0, 1, 0); } else if (sscanf(buffer, "/fcirclez %d", &radius) == 1) { + // Place a filled circle on the z-axis with a radius sphere(&g->block0, radius, 1, 0, 0, 1); } else if (sscanf(buffer, "/circlez %d", &radius) == 1) { + // Place an unfilled circle on the z-axis with a radius sphere(&g->block0, radius, 0, 0, 0, 1); } else if (sscanf(buffer, "/fcylinder %d", &radius) == 1) { + // Place a filled cylinder with a radius cylinder(&g->block0, &g->block1, radius, 1); } else if (sscanf(buffer, "/cylinder %d", &radius) == 1) { + // Place an unfilled cylinder with a radius cylinder(&g->block0, &g->block1, radius, 0); } else if (forward) { + // If no command was found, maybe send it as a chat message client_talk(buffer); } } +// Arguments: none +// Returns: none void on_light() { State *s = &g->players->state; int hx, hy, hz; @@ -2137,6 +2807,8 @@ void on_light() { } } +// Arguments: none +// Returns: none void on_left_click() { State *s = &g->players->state; int hx, hy, hz; @@ -2150,6 +2822,8 @@ void on_left_click() { } } +// Arguments: none +// Returns: none void on_right_click() { State *s = &g->players->state; int hx, hy, hz; @@ -2162,6 +2836,8 @@ void on_right_click() { } } +// Arguments: none +// Returns: none void on_middle_click() { State *s = &g->players->state; int hx, hy, hz; @@ -2174,6 +2850,14 @@ void on_middle_click() { } } +// Handle key press callback +// Arguments: +// - window +// - key +// - scancode +// - action +// - mods +// Returns: none void on_key(GLFWwindow *window, int key, int scancode, int action, int mods) { int control = mods & (GLFW_MOD_CONTROL | GLFW_MOD_SUPER); int exclusive = @@ -2274,6 +2958,11 @@ void on_key(GLFWwindow *window, int key, int scancode, int action, int mods) { } } +// Handle character callback +// Arguments: +// - window +// - u +// Returns: none void on_char(GLFWwindow *window, unsigned int u) { if (g->suppress_char) { g->suppress_char = 0; @@ -2307,6 +2996,12 @@ void on_char(GLFWwindow *window, unsigned int u) { } } +// Handle mouse scroll callback, scroll through block items +// Arguments: +// - window: window the click happened in +// - xdelta: scroll x change since last time +// - ydelta: scroll y change since last time +// Returns: none void on_scroll(GLFWwindow *window, double xdelta, double ydelta) { static double ypos = 0; ypos += ydelta; @@ -2323,6 +3018,13 @@ void on_scroll(GLFWwindow *window, double xdelta, double ydelta) { } } +// Handle mouse button press callback +// Arguments: +// - window: window the click happened in +// - button +// - action +// - mods +// Returns: none void on_mouse_button(GLFWwindow *window, int button, int action, int mods) { int control = mods & (GLFW_MOD_CONTROL | GLFW_MOD_SUPER); int exclusive = @@ -2332,6 +3034,7 @@ void on_mouse_button(GLFWwindow *window, int button, int action, int mods) { } if (button == GLFW_MOUSE_BUTTON_LEFT) { if (exclusive) { + // Control + left click simulates a right click if (control) { on_right_click(); } @@ -2360,6 +3063,8 @@ void on_mouse_button(GLFWwindow *window, int button, int action, int mods) { } } +// Arguments: none +// Returns: none void create_window() { int window_width = WINDOW_WIDTH; int window_height = WINDOW_HEIGHT; @@ -2375,6 +3080,8 @@ void create_window() { window_width, window_height, "Craft", monitor, NULL); } +// Arguments: none +// Returns: none void handle_mouse_input() { int exclusive = glfwGetInputMode(g->window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; @@ -2408,6 +3115,9 @@ void handle_mouse_input() { } } +// Arguments: +// - dt: delta time +// Returns: none void handle_movement(double dt) { static float dy = 0; State *s = &g->players->state; @@ -2468,14 +3178,39 @@ void handle_movement(double dt) { } } +// Parse response string from server +// Arguments: +// - buffer: the response string to parse +// Returns: none +// Note: +// multiplayer: A simple, ASCII, line-based protocol is used. Each line is +// made up of a command code and zero or more comma-separated arguments. +// Server Commands/responses: +// - B,p,q,x,y,z,w : block update in chunk (p, q) at (x, y, z) of block +// type "w" +// - D,pid : delete player with id "pid" +// - E,e,d : time elapse "e" with day length "d" +// - K,p,q,key : set "key" for chunk (p, q) +// - L,p,q,x,y,z,w : light update in chunk (p, q) at (x, y, z) of block +// type "w" +// - N,pid,name : player name for player with id +// - P,pid,x,y,z,rx,ry : player movement update +// - R,p,q : mark chunk at (p, q) as dirty +// - S,p,q,x,y,z,face,text : sign placement +// - T,s : chat message "s" +// - U,pid,x,y,z,rx,ry : response to set this client's player position +// (maybe upon joining the server?) void parse_buffer(char *buffer) { Player *me = g->players; State *s = &g->players->state; char *key; char *line = tokenize(buffer, "\n", &key); while (line) { + // Try and parse this line as a server response/command + // If the response does not match anything, the line is ignored. int pid; float ux, uy, uz, urx, ury; + // Set this client's player position if (sscanf(line, "U,%d,%f,%f,%f,%f,%f", &pid, &ux, &uy, &uz, &urx, &ury) == 6) { @@ -2486,6 +3221,7 @@ void parse_buffer(char *buffer) { s->y = highest_block(s->x, s->z) + 2; } } + // Block update int bp, bq, bx, by, bz, bw; if (sscanf(line, "B,%d,%d,%d,%d,%d,%d", &bp, &bq, &bx, &by, &bz, &bw) == 6) @@ -2495,11 +3231,13 @@ void parse_buffer(char *buffer) { s->y = highest_block(s->x, s->z) + 2; } } + // Light update if (sscanf(line, "L,%d,%d,%d,%d,%d,%d", &bp, &bq, &bx, &by, &bz, &bw) == 6) { set_light(bp, bq, bx, by, bz, bw); } + // Player position update float px, py, pz, prx, pry; if (sscanf(line, "P,%d,%f,%f,%f,%f,%f", &pid, &px, &py, &pz, &prx, &pry) == 6) @@ -2517,19 +3255,23 @@ void parse_buffer(char *buffer) { update_player(player, px, py, pz, prx, pry, 1); } } + // Delete player id if (sscanf(line, "D,%d", &pid) == 1) { delete_player(pid); } + // Chunk key int kp, kq, kk; if (sscanf(line, "K,%d,%d,%d", &kp, &kq, &kk) == 3) { db_set_key(kp, kq, kk); } + // Dirty chunk if (sscanf(line, "R,%d,%d", &kp, &kq) == 2) { Chunk *chunk = find_chunk(kp, kq); if (chunk) { dirty_chunk(chunk); } } + // Time sync double elapsed; int day_length; if (sscanf(line, "E,%lf,%d", &elapsed, &day_length) == 2) { @@ -2537,11 +3279,13 @@ void parse_buffer(char *buffer) { g->day_length = day_length; g->time_changed = 1; } + // Chat message if (line[0] == 'T' && line[1] == ',') { char *text = line + 2; add_message(text); } char format[64]; + // Player name snprintf( format, sizeof(format), "N,%%d,%%%ds", MAX_NAME_LENGTH - 1); char name[MAX_NAME_LENGTH]; @@ -2551,6 +3295,7 @@ void parse_buffer(char *buffer) { strncpy(player->name, name, MAX_NAME_LENGTH); } } + // Sign placement snprintf( format, sizeof(format), "S,%%d,%%d,%%d,%%d,%%d,%%d,%%%d[^\n]", MAX_SIGN_LENGTH - 1); @@ -2561,10 +3306,16 @@ void parse_buffer(char *buffer) { { _set_sign(bp, bq, bx, by, bz, face, text, 0); } + // Get next line line = tokenize(NULL, "\n", &key); } } +// Reset the game model +// Arguments: none +// Returns: +// - no return value +// - modifies game model void reset_model() { memset(g->chunks, 0, sizeof(Chunk) * MAX_CHUNKS); g->chunk_count = 0; @@ -2583,6 +3334,12 @@ void reset_model() { g->time_changed = 1; } +// Main program code +// Arguments: +// - argc +// - argv +// Returns: +// - zero upon success, non-zero upon failure int main(int argc, char **argv) { // INITIALIZATION // curl_global_init(CURL_GLOBAL_DEFAULT); @@ -2599,6 +3356,7 @@ int main(int argc, char **argv) { return -1; } + // Initialize the graphical user interface with GLFW glfwMakeContextCurrent(g->window); glfwSwapInterval(VSYNC); glfwSetInputMode(g->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); @@ -2611,6 +3369,7 @@ int main(int argc, char **argv) { return -1; } + // Initialize some OpenGL settings glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glLogicOp(GL_INVERT); @@ -2711,6 +3470,7 @@ int main(int argc, char **argv) { snprintf(g->db_path, MAX_PATH_LENGTH, "%s", DB_PATH); } + // Configure game radius settings g->create_radius = CREATE_CHUNK_RADIUS; g->render_radius = RENDER_CHUNK_RADIUS; g->delete_radius = DELETE_CHUNK_RADIUS; @@ -2727,12 +3487,16 @@ int main(int argc, char **argv) { } // OUTER LOOP // + // This outer loop is necessary because the game can switch between online + // and offline mode any time and needs to shutdown and re-init the db and + // other resources. int running = 1; while (running) { // DATABASE INITIALIZATION // if (g->mode == MODE_OFFLINE || USE_CACHE) { db_enable(); if (db_init(g->db_path)) { + // db initialization failed return -1; } if (g->mode == MODE_ONLINE) { @@ -2757,6 +3521,7 @@ int main(int argc, char **argv) { double last_update = glfwGetTime(); GLuint sky_buffer = gen_sky_buffer(); + // Init local player Player *me = g->players; State *s = &g->players->state; me->id = 0; @@ -2834,6 +3599,7 @@ int main(int argc, char **argv) { glClear(GL_DEPTH_BUFFER_BIT); render_sky(&sky_attrib, player, sky_buffer); glClear(GL_DEPTH_BUFFER_BIT); + // Get the face count while rendering for displaying the number on screen int face_count = render_chunks(&block_attrib, player); render_signs(&text_attrib, player); render_sign(&text_attrib, player); @@ -2856,6 +3622,7 @@ int main(int argc, char **argv) { float ts = 12 * g->scale; float tx = ts / 2; float ty = g->height - ts; + // Technical info text if (SHOW_INFO_TEXT) { int hour = time_of_day() * 24; char am_pm = hour < 12 ? 'a' : 'p'; @@ -2870,6 +3637,7 @@ int main(int argc, char **argv) { render_text(&text_attrib, ALIGN_LEFT, tx, ty, ts, text_buffer); ty -= ts * 2; } + // Chat text if (SHOW_CHAT_TEXT) { for (int i = 0; i < MAX_MESSAGES; i++) { int index = (g->message_index + i) % MAX_MESSAGES; @@ -2880,6 +3648,7 @@ int main(int argc, char **argv) { } } } + // Current typing text if (g->typing) { snprintf(text_buffer, 1024, "> %s", g->typing_buffer); render_text(&text_attrib, ALIGN_LEFT, tx, ty, ts, text_buffer); @@ -2936,10 +3705,14 @@ int main(int argc, char **argv) { // SWAP AND POLL // glfwSwapBuffers(g->window); glfwPollEvents(); + // When closing the window, break this inner loop to shutdown and do + // not re-init. if (glfwWindowShouldClose(g->window)) { running = 0; break; } + // If online/offline mode changed, break this inner loop to shutdown + // and re-init. if (g->mode_changed) { g->mode_changed = 0; break; @@ -2947,6 +3720,8 @@ int main(int argc, char **argv) { } // SHUTDOWN // + // Shutdown of current game mode + // (The outer game loop may or may not continue after this) db_save_state(s->x, s->y, s->z, s->rx, s->ry); db_close(); db_disable(); @@ -2957,7 +3732,9 @@ int main(int argc, char **argv) { delete_all_players(); } + // Final program closing glfwTerminate(); curl_global_cleanup(); return 0; } + diff --git a/src/map.c b/src/map.c index e2258a0b3..f4e110156 100644 --- a/src/map.c +++ b/src/map.c @@ -2,6 +2,8 @@ #include #include "map.h" +// This file contains code for a hash map that maps (x, y, z) locations to w block ids + int hash_int(int key) { key = ~key + (key << 15); key = key ^ (key >> 12); @@ -112,3 +114,4 @@ void map_grow(Map *map) { map->size = new_map.size; map->data = new_map.data; } + diff --git a/src/map.h b/src/map.h index 8801fb2be..e539544df 100644 --- a/src/map.h +++ b/src/map.h @@ -16,6 +16,14 @@ #define END_MAP_FOR_EACH } +// Data value entry in the Map structure +// UNION of: +// - value +// OR: +// - x block x position +// - y block y position +// - z block z position +// - w: block id (or light value?) typedef union { unsigned int value; struct { diff --git a/src/matrix.c b/src/matrix.c index 73073c6b7..8eca59096 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -3,11 +3,18 @@ #include "matrix.h" #include "util.h" +// Normalize a vector, (x, y, z) +// (So it has magnitude of 1). void normalize(float *x, float *y, float *z) { float d = sqrtf((*x) * (*x) + (*y) * (*y) + (*z) * (*z)); *x /= d; *y /= d; *z /= d; } +// Set the given matrix reference to a 4x4 identity matrix +// ( 1 0 0 0 ) +// ( 0 1 0 0 ) +// ( 0 0 1 0 ) +// ( 0 0 0 1 ) void mat_identity(float *matrix) { matrix[0] = 1; matrix[1] = 0; @@ -208,6 +215,19 @@ void set_matrix_2d(float *matrix, int width, int height) { mat_ortho(matrix, 0, width, 0, height, -1, 1); } +// Set matrix reference to a 3D view, either orthographic or perspective. +// Aguments: +// - matrix: matrix to modify +// - width: target viewport width +// - height: target viewport height +// - x +// - y +// - z +// - rx: rotation x +// - ry: rotation y +// - fov: field of view +// - ortho: flag to indicate an orthographic view +// - radius void set_matrix_3d( float *matrix, int width, int height, float x, float y, float z, float rx, float ry, @@ -257,3 +277,4 @@ void set_matrix_item(float *matrix, int width, int height, int scale) { mat_identity(matrix); mat_multiply(matrix, a, matrix); } + diff --git a/src/ring.c b/src/ring.c index 88335f498..7276836d1 100644 --- a/src/ring.c +++ b/src/ring.c @@ -2,6 +2,15 @@ #include #include "ring.h" +// Ring data structure for setting up a sequence of tasks for workers to do to +// the world database. + +// Allocate ring with a set initial capacity (which can grow) +// Arguments: +// - ring: pointer to ring structure to modify +// - capacity: initial capacity for ring entries +// Returns: +// - modifies the structure that ring points to void ring_alloc(Ring *ring, int capacity) { ring->capacity = capacity; ring->start = 0; @@ -9,18 +18,37 @@ void ring_alloc(Ring *ring, int capacity) { ring->data = (RingEntry *)calloc(capacity, sizeof(RingEntry)); } +// Free the ring's data (but does not free the given ring pointer). +// Arguments: +// - ring: pointer to ring structure +// Returns: none void ring_free(Ring *ring) { free(ring->data); } +// Predicate function for if the ring is empty +// Arguments: +// - ring: pointer to ring structure +// Returns: +// - returns non-zero if ring is empty int ring_empty(Ring *ring) { return ring->start == ring->end; } +// Predicate function for if the ring is full +// Arguments: +// - ring: pointer to ring structure +// Returns: +// - returns non-zero if ring is full int ring_full(Ring *ring) { return ring->start == (ring->end + 1) % ring->capacity; } +// Get the ring's number of current entries +// Arguments: +// - ring: pointer to ring structure +// Returns: +// - returns the number of entries int ring_size(Ring *ring) { if (ring->end >= ring->start) { return ring->end - ring->start; @@ -30,29 +58,59 @@ int ring_size(Ring *ring) { } } +// Grow the ring's memory space for ring entries. +// Note: this function is mutually recursive with ring_put +// Arguments: +// - ring: pointer to ring structure to modify +// Returns: +// - modifies the structure that ring points to void ring_grow(Ring *ring) { Ring new_ring; RingEntry entry; + // Create a new ring with double the capacity ring_alloc(&new_ring, ring->capacity * 2); + // Copy the entries from the old ring to the new while (ring_get(ring, &entry)) { ring_put(&new_ring, &entry); } + // Free the old ring's data free(ring->data); + // Make the old ring have the new ring's values ring->capacity = new_ring.capacity; ring->start = new_ring.start; ring->end = new_ring.end; ring->data = new_ring.data; } +// Put an entry into the ring. +// Note: this function is mutually recursive with ring_grow +// Arguments: +// - ring: pointer to ring structure to modify +// Returns: +// - modifies the structure that ring points to void ring_put(Ring *ring, RingEntry *entry) { + // Ensure there is room for a new entry if (ring_full(ring)) { ring_grow(ring); } + // Copy the new entry to the end of the ring RingEntry *e = ring->data + ring->end; memcpy(e, entry, sizeof(RingEntry)); + // Increment the end pointer ring->end = (ring->end + 1) % ring->capacity; } +// Put a block entry into the ring. +// Arguments: +// - ring: pointer to ring structure to modify +// - p: chunk x position +// - q: chunk z position +// - x: block x position +// - y: block y position +// - z: block z position +// - w: block type to set +// Returns: +// - modifies the structure that ring points to void ring_put_block(Ring *ring, int p, int q, int x, int y, int z, int w) { RingEntry entry; entry.type = BLOCK; @@ -65,6 +123,17 @@ void ring_put_block(Ring *ring, int p, int q, int x, int y, int z, int w) { ring_put(ring, &entry); } +// Put a light entry into the ring. +// Arguments: +// - ring: pointer to ring structure to modify +// - p: chunk x position +// - q: chunk z position +// - x: light x position +// - y: light y position +// - z: light z position +// - w: light value to set +// Returns: +// - modifies the structure that ring points to void ring_put_light(Ring *ring, int p, int q, int x, int y, int z, int w) { RingEntry entry; entry.type = LIGHT; @@ -77,6 +146,14 @@ void ring_put_light(Ring *ring, int p, int q, int x, int y, int z, int w) { ring_put(ring, &entry); } +// Put a key entry into the ring. +// Arguments: +// - ring: pointer to ring structure to modify +// - p: chunk x position +// - q: chunk z position +// - key: key value to set +// Returns: +// - modifies the structure that ring points to void ring_put_key(Ring *ring, int p, int q, int key) { RingEntry entry; entry.type = KEY; @@ -86,18 +163,36 @@ void ring_put_key(Ring *ring, int p, int q, int key) { ring_put(ring, &entry); } +// Put a commit entry into the ring. +// Arguments: +// - ring: pointer to ring structure to modify +// Returns: +// - modifies the structure that ring points to void ring_put_commit(Ring *ring) { RingEntry entry; entry.type = COMMIT; ring_put(ring, &entry); } +// Put a exit entry into the ring. +// Arguments: +// - ring: pointer to ring structure to modify +// Returns: +// - modifies the structure that ring points to void ring_put_exit(Ring *ring) { RingEntry entry; entry.type = EXIT; ring_put(ring, &entry); } +// Retrieves and removes the next RingEntry from the ring and copies it to the +// entry argument. +// Arguments: +// - ring: pointer to ring structure to modify +// Returns: +// - returns 0 if an entry was not retrieved +// - returns 1 if an entry was retrieved +// - modifies the structure that ring points to int ring_get(Ring *ring, RingEntry *entry) { if (ring_empty(ring)) { return 0; @@ -107,3 +202,4 @@ int ring_get(Ring *ring, RingEntry *entry) { ring->start = (ring->start + 1) % ring->capacity; return 1; } + diff --git a/src/sign.c b/src/sign.c index 2eebb369f..1cb18e706 100644 --- a/src/sign.c +++ b/src/sign.c @@ -2,16 +2,33 @@ #include #include "sign.h" +// Allocate room within the given list structure for "capacity" signs +// Allocates memory. +// Arguments: +// - list: the already initialized sign list to allocate data for +// - capacity: the number of signs to allocate room for +// Returns: +// - modifies the structure pointed to by list void sign_list_alloc(SignList *list, int capacity) { list->capacity = capacity; list->size = 0; list->data = (Sign *)calloc(capacity, sizeof(Sign)); } +// Free the SignList's data. +// Arguments: +// - list: list pointer whose data will be free'd +// Returns: none void sign_list_free(SignList *list) { free(list->data); } +// Grow the SignList's data and capacity so there is more room for signs. +// Allocates memory. +// Arguments: +// - list: sign list to grow +// Returns: +// - modifies the structure pointed to by list void sign_list_grow(SignList *list) { SignList new_list; sign_list_alloc(&new_list, list->capacity * 2); @@ -21,6 +38,15 @@ void sign_list_grow(SignList *list) { list->data = new_list.data; } +// Meant to be called by sign_list_add(). +// Actually adds a sign and doesn't check for signs on the same block and face +// which should be removed. +// Note: may grow the sign list, allocating memory. +// Arguments: +// - list: SignList to modify and add to +// - sign: sign data to read +// Returns: +// - modifies the structure pointed to by list void _sign_list_add(SignList *list, Sign *sign) { if (list->size == list->capacity) { sign_list_grow(list); @@ -29,6 +55,18 @@ void _sign_list_add(SignList *list, Sign *sign) { memcpy(e, sign, sizeof(Sign)); } +// Add or overwrite a sign into the sign list at block position and face. +// Does not create multiple signs in one location (at the same block & face). +// Note: may allocate memory when growing the sign list. +// Arguments: +// - list: sign list to add the sign to +// - x: block x position to remove signs from +// - y: block y position to remove signs from +// - z: block z position to remove signs from +// - face: the face of the block to remove signs from +// - text: the signs text (a maximum length of MAX_SIGN_LENGTH chars will be used) +// Returns: +// - modifies the structure pointed to by list void sign_list_add( SignList *list, int x, int y, int z, int face, const char *text) { @@ -43,6 +81,16 @@ void sign_list_add( _sign_list_add(list, &sign); } +// Remove all signs from the sign list at the given block position and face. +// Arguments: +// - list: sign list to search and remove signs from +// - x: block x position to remove signs from +// - y: block y position to remove signs from +// - z: block z position to remove signs from +// - face: the face of the block to remove signs from +// Returns: +// - returns the number of signs removed +// - modifies the structure pointed to by list int sign_list_remove(SignList *list, int x, int y, int z, int face) { int result = 0; for (int i = 0; i < list->size; i++) { @@ -57,6 +105,15 @@ int sign_list_remove(SignList *list, int x, int y, int z, int face) { return result; } +// Remove all signs from the sign list at the given block position. +// Arguments: +// - list: sign list to search and remove signs from +// - x: block x position to remove signs from +// - y: block y position to remove signs from +// - z: block z position to remove signs from +// Returns: +// - returns the number of signs removed +// - modifies the structure pointed to by list int sign_list_remove_all(SignList *list, int x, int y, int z) { int result = 0; for (int i = 0; i < list->size; i++) { @@ -70,3 +127,4 @@ int sign_list_remove_all(SignList *list, int x, int y, int z) { } return result; } + diff --git a/src/util.c b/src/util.c index 9e1d0b1f0..23f48feaf 100644 --- a/src/util.c +++ b/src/util.c @@ -6,16 +6,31 @@ #include "matrix.h" #include "util.h" +// Return pseudo-random number between 0 and "n". +// Arguments: +// - n: maximum random value +// Returns: +// - a pseudo-random integer int rand_int(int n) { int result; while (n <= (result = rand() / (RAND_MAX / n))); return result; } +// Return pseudo-random number between 0.0 and 1.0. +// Arguments: none +// Returns: +// - pseudo-random value between 0.0 and 1.0 double rand_double() { return (double)rand() / (double)RAND_MAX; } +// Update frames per second info +// Arguments: +// - fps: fps context pointer +// Returns: +// - no return value +// - modifies fps context void update_fps(FPS *fps) { fps->frames++; double now = glfwGetTime(); @@ -27,21 +42,35 @@ void update_fps(FPS *fps) { } } +// Load all file data from path +// Arguments: +// - path: file path to open and to load data from +// Returns: +// - a newly allocated data pointer which must be free'd char *load_file(const char *path) { FILE *file = fopen(path, "rb"); if (!file) { - fprintf(stderr, "fopen %s failed: %d %s\n", path, errno, strerror(errno)); + fprintf(stderr, "fopen %s failed: %d %s\n", + path, errno, strerror(errno)); exit(1); } + // Get file content's size fseek(file, 0, SEEK_END); int length = ftell(file); rewind(file); + // Allocate space and read in the data char *data = calloc(length + 1, sizeof(char)); fread(data, 1, length, file); fclose(file); return data; } +// Create a single OpenGL data buffer and add data +// Arguments: +// - size +// - data +// Returns: +// - new OpenGL buffer handle GLuint gen_buffer(GLsizei size, GLfloat *data) { GLuint buffer; glGenBuffers(1, &buffer); @@ -51,14 +80,32 @@ GLuint gen_buffer(GLsizei size, GLfloat *data) { return buffer; } +// Delete a single OpenGL data buffer +// Arguments: +// - buffer: OpenGL buffer handle of the buffer to delete +// Returns: none void del_buffer(GLuint buffer) { glDeleteBuffers(1, &buffer); } +// Allocate memory for faces where each face has a certain number +// of float component properties. +// Arguments: +// - components: the number of components each face has +// - faces: number of faces +// Returns: +// - newly allocated space for floats GLfloat *malloc_faces(int components, int faces) { return malloc(sizeof(GLfloat) * 6 * components * faces); } +// Create and initialize face data +// Arguments: +// - components: number of components per face +// - faces: number of faces +// - data: data to bind to OpenGL context +// Returns: +// - OpenGL buffer handle GLuint gen_faces(int components, int faces, GLfloat *data) { GLuint buffer = gen_buffer( sizeof(GLfloat) * 6 * components * faces, data); @@ -66,10 +113,17 @@ GLuint gen_faces(int components, int faces, GLfloat *data) { return buffer; } +// Create a shader program from its source code +// Arguments: +// - type: vertex or fragment shader +// - source: program source code string +// Returns: +// - OpenGL shader handle GLuint make_shader(GLenum type, const char *source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); + // Get shader status so we can print an error if compiling it failed GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { @@ -83,6 +137,12 @@ GLuint make_shader(GLenum type, const char *source) { return shader; } +// Load a shader program from a file +// Arguments: +// - type: vertex or fragment shader +// - path: file path to load the shader program from +// Returns: +// - OpenGL shader handle GLuint load_shader(GLenum type, const char *path) { char *data = load_file(path); GLuint result = make_shader(type, data); @@ -90,6 +150,13 @@ GLuint load_shader(GLenum type, const char *path) { return result; } + +// Arguments: +// - shader1: a shader handle to attach +// - shader2: a shader handle to attach +// Returns: +// - returns OpenGL program handle +// - deletes shader1 and shader2 from the context GLuint make_program(GLuint shader1, GLuint shader2) { GLuint program = glCreateProgram(); glAttachShader(program, shader1); @@ -112,6 +179,12 @@ GLuint make_program(GLuint shader1, GLuint shader2) { return program; } +// Loads a shader program from files. +// Arguments: +// - path1 : vertex shader file path +// - path2 : fragment shader file path +// Returns: +// - OpenGL program handle GLuint load_program(const char *path1, const char *path2) { GLuint shader1 = load_shader(GL_VERTEX_SHADER, path1); GLuint shader2 = load_shader(GL_FRAGMENT_SHADER, path2); @@ -119,6 +192,15 @@ GLuint load_program(const char *path1, const char *path2) { return program; } +// Flip an image vertically +// Notes: assumes 4 channels per image pixel. +// Arguments: +// - data: image pixel data +// - width: image width +// - height: image height +// Returns: +// - no return value +// - modifies data void flip_image_vertical( unsigned char *data, unsigned int width, unsigned int height) { @@ -133,39 +215,72 @@ void flip_image_vertical( free(new_data); } +// Loads a PNG file as a 2D texture for the current OpenGL texture context. +// Arguments: +// - file_name: the png file to load the texture from +// Returns: +// - no return value +// - modifies OpenGL state by loading the image data into the current 2D texture void load_png_texture(const char *file_name) { unsigned int error; unsigned char *data; unsigned int width, height; error = lodepng_decode32_file(&data, &width, &height, file_name); if (error) { - fprintf(stderr, "load_png_texture %s failed, error %u: %s\n", file_name, error, lodepng_error_text(error)); + fprintf(stderr, "load_png_texture %s failed, error %u: %s\n", + file_name, error, lodepng_error_text(error)); exit(1); } flip_image_vertical(data, width, height); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + // Free the data because lodepng_decode32_file() allocated it, + // and we copied the data over to OpenGL. free(data); } +// Tokenize a string by a delimiter +// NOTE: may modify data in "str" +// Arguments: +// - str: input string to tokenize (may be modified). If the argument "str" is +// NULL, then tokenization continues from where the argument "key" points to. +// - delim: null-terminated character list of delimiter characters to use as +// token separators +// - key: pointer to keep track of where in the string is currently being +// tokenized +// Returns: +// - a null-terminated C string token +// - modifies "str" so that there are null-terminators after each token +// - modifies where "key" points to char *tokenize(char *str, const char *delim, char **key) { char *result; + // If str is not given, use the position saved in key if (str == NULL) { str = *key; } + // Skip over the length of the delimiter substring found str += strspn(str, delim); if (*str == '\0') { return NULL; } result = str; str += strcspn(str, delim); + // Null-terminate the current token so that the returned tokens + // are always proper C strings. if (*str) { *str++ = '\0'; } + // Save the current token position to key *key = str; return result; } +// Calculate width of an ASCII character +// NOTE: expects input character value to be in the range 0 to 128. +// Arguments: +// - input: character +// Returns: +// - character width in screen space int char_width(char input) { static const int lookup[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -180,7 +295,13 @@ int char_width(char input) { return lookup[input]; } +// Calculate width of a text string in screen space +// Arguments: +// - input: null-terminated string to measure +// Returns: +// - total string width in screen space int string_width(const char *input) { + // Sum up the character width of each character in the input string int result = 0; int length = strlen(input); for (int i = 0; i < length; i++) { @@ -189,21 +310,39 @@ int string_width(const char *input) { return result; } +// Wrap text using the maximum line width based on character size +// Arguments: +// - input: input text to wrap +// - max_width: maximum string line width, in screen space +// - output: output string buffer +// - max_length: maximum length to write to the string buffer +// Returns: +// - output: character buffer is written to +// - the number of lines that the output text uses. int wrap(const char *input, int max_width, char *output, int max_length) { + // Always terminate the output string, in case there are no tokens *output = '\0'; + // Create a copy of the input string because tokenization modifies the given + // string in order to add null-termination to the end of tokens. char *text = malloc(sizeof(char) * (strlen(input) + 1)); strcpy(text, input); int space_width = char_width(' '); + // Count the number of lines created int line_number = 0; char *key1, *key2; + // Begin tokenizing each line by line breaks char *line = tokenize(text, "\r\n", &key1); while (line) { + // Keep track of the current line width in screen space int line_width = 0; + // Begin tokenizing each word by spaces char *token = tokenize(line, " ", &key2); while (token) { int token_width = string_width(token); if (line_width) { if (line_width + token_width > max_width) { + // If the width goes over the max line width, create a new + // line. line_width = 0; line_number++; strncat(output, "\n", max_length - strlen(output) - 1); @@ -214,12 +353,16 @@ int wrap(const char *input, int max_width, char *output, int max_length) { } strncat(output, token, max_length - strlen(output) - 1); line_width += token_width + space_width; + // Next word token token = tokenize(NULL, " ", &key2); } line_number++; strncat(output, "\n", max_length - strlen(output) - 1); + // Next line token line = tokenize(NULL, "\r\n", &key1); } + // Free the copy of the input text free(text); return line_number; } + diff --git a/src/util.h b/src/util.h index a7bee9934..ee3eab614 100644 --- a/src/util.h +++ b/src/util.h @@ -19,6 +19,10 @@ #define LOG(...) #endif +// Frames-per-second context +// - fps: frames per second +// - frames: number of frames collected (since last time) +// - since: the last time the frames per second was updated typedef struct { unsigned int fps; unsigned int frames; diff --git a/src/world.c b/src/world.c index 8ca739821..65de93352 100644 --- a/src/world.c +++ b/src/world.c @@ -2,30 +2,41 @@ #include "noise.h" #include "world.h" +// Main terrain generation function +// Parameters: +// - p: chunk p location +// - q: chunk q location +// - func: function callback to modify blocks in the world (see world.h) +// - arg: last argument to be used for the function callback void create_world(int p, int q, world_func func, void *arg) { + // GUESS: the inclusion of the extra pad locations is for tree generation across chunk borders. int pad = 1; + // Loop for each (x, z) location in chunk (p, q): for (int dx = -pad; dx < CHUNK_SIZE + pad; dx++) { for (int dz = -pad; dz < CHUNK_SIZE + pad; dz++) { + // Why is flag either +1 or -1? int flag = 1; if (dx < 0 || dz < 0 || dx >= CHUNK_SIZE || dz >= CHUNK_SIZE) { flag = -1; } - int x = p * CHUNK_SIZE + dx; - int z = q * CHUNK_SIZE + dz; + int x = p * CHUNK_SIZE + dx; // convert p (chunk x) and dx to world x + int z = q * CHUNK_SIZE + dz; // convert q (chunk z) and dz to world z float f = simplex2(x * 0.01, z * 0.01, 4, 0.5, 2); float g = simplex2(-x * 0.01, -z * 0.01, 2, 0.9, 2); int mh = g * 32 + 16; int h = f * mh; - int w = 1; + // w = block id + int w = 1; // grass int t = 12; if (h <= t) { h = t; - w = 2; + w = 2; // sand } // sand and grass terrain for (int y = 0; y < h; y++) { func(x, y, z, w * flag, arg); } + // Place plants on grass (block id 1). if (w == 1) { if (SHOW_PLANTS) { // grass @@ -68,6 +79,7 @@ void create_world(int p, int q, world_func func, void *arg) { if (simplex3( x * 0.01, y * 0.1, z * 0.01, 8, 0.5, 2) > 0.75) { + // Block id 16 is cloud func(x, y, z, 16 * flag, arg); } } diff --git a/src/world.h b/src/world.h index 7953d33fb..9db133afb 100644 --- a/src/world.h +++ b/src/world.h @@ -1,7 +1,8 @@ #ifndef _world_h_ #define _world_h_ -typedef void (*world_func)(int, int, int, int, void *); +// World function callback signature (used to modify a map's blocks) +typedef void (*world_func)(int x, int y, int z, int w, void *arg); void create_world(int p, int q, world_func func, void *arg);