-Subproject commit d439328eed1446e6869a1fd23fe5c0a99350806d
+Subproject commit 8878e826fa3e7b1231e652fc13d11c7a61629d13
client/cube.c
client/debug_menu.c
client/facecache.c
+ client/facedir.c
client/font.c
client/frustum.c
client/game.c
server/schematic.c
server/server.c
server/server_config.c
+ server/server_item.c
server/server_player.c
server/server_terrain.c
server/terrain_gen.c
.view_distance = 255.0,
.vsync = true,
.meshgen_threads = 4,
+ .swap_mouse_buttons = true,
};
-#define NUM_CONFIG_ENTRIES 5
-static ConfigEntry config_entries[NUM_CONFIG_ENTRIES] = {
+static ConfigEntry config_entries[] = {
{
.type = CONFIG_UINT,
.key = "antialiasing",
.key = "meshgen_threads",
.value = &client_config.meshgen_threads,
},
+ {
+ .type = CONFIG_BOOL,
+ .key = "swap_mouse_buttons",
+ .value = &client_config.meshgen_threads,
+ },
};
__attribute__((constructor)) static void client_config_init()
{
- config_read("client.conf", config_entries, NUM_CONFIG_ENTRIES);
+ config_read("client.conf", config_entries, sizeof config_entries / sizeof *config_entries);
}
__attribute__((destructor)) static void client_config_deinit()
{
- config_free(config_entries, NUM_CONFIG_ENTRIES);
+ config_free(config_entries, sizeof config_entries / sizeof *config_entries);
}
double view_distance;
bool vsync;
unsigned int meshgen_threads;
+ bool swap_mouse_buttons;
} client_config;
#endif
if (arm) {
arm->rot.x = mesh ? -M_PI / 8.0 : 0.0;
- model_node_transform(arm);
+ model_node_transform(arm);
}
}
{
ClientPlayerData *data = entity->extra;
+ pthread_mutex_init(&data->mtx_inv, NULL);
+
item_stack_initialize(&data->inventory.left);
item_stack_initialize(&data->inventory.right);
{
ClientPlayerData *data = entity->extra;
+ pthread_mutex_destroy(&data->mtx_inv);
+
item_stack_destroy(&data->inventory.left);
item_stack_destroy(&data->inventory.right);
}
return;
ClientPlayerData *data = entity->extra;
+ pthread_mutex_lock(&data->mtx_inv);
item_stack_deserialize(&data->inventory.left, &pkt->left);
item_stack_deserialize(&data->inventory.right, &pkt->right);
wield_update(data->bones.hand_left, data->bones.arm_left, data->inventory.left.type);
wield_update(data->bones.hand_right, data->bones.arm_right, data->inventory.right.type);
+ pthread_mutex_unlock(&data->mtx_inv);
refcount_drp(&entity->rc);
}
#include "client/client_item.h"
+#include "client/interact.h"
+#include "node.h"
+
+static bool use_dig(__attribute__((unused)) ItemStack *stack)
+{
+ return interact_pointed.exists
+ && (node_defs[interact_pointed.node].dig_class & item_defs[stack->type].dig_class);
+}
ClientItemDef client_item_defs[COUNT_ITEM] = {
// unknown
{
.mesh_path = RESSOURCE_PATH "meshes/unknown.txt",
.mesh = {0},
+ .use = NULL,
},
// none
{
.mesh_path = NULL,
.mesh = {0},
+ .use = NULL,
},
// pickaxe
{
.mesh_path = RESSOURCE_PATH "meshes/pickaxe.txt",
.mesh = {0},
+ .use = &use_dig,
},
// axe
{
.mesh_path = RESSOURCE_PATH "meshes/axe.txt",
.mesh = {0},
+ .use = &use_dig,
},
};
#ifndef _CLIENT_ITEM_H_
#define _CLIENT_ITEM_H_
+#include <stdbool.h>
#include "client/mesh.h"
#include "item.h"
+#include "node.h"
typedef struct {
const char *mesh_path;
Mesh mesh;
+ bool (*use)(ItemStack *stack);
} ClientItemDef;
extern ClientItemDef client_item_defs[];
{
ClientEntity *entity = client_entity_grab(id);
- if (entity->type == &client_entity_types[ENTITY_LOCALPLAYER]
+ if (entity->type == &client_entity_types[ENTITY_LOCALPLAYER]
|| entity->type == &client_entity_types[ENTITY_PLAYER])
return entity;
ItemStack left;
ItemStack right;
} inventory;
+ pthread_mutex_t mtx_inv;
struct ClientPlayerBones {
ModelNode *nametag;
ModelNode *neck;
#include <stdlib.h>
#include <pthread.h>
#include "client/client.h"
+#include "client/facedir.h"
#include "client/facecache.h"
#include "client/client_config.h"
#include "client/client_player.h"
meta->sync = 0;
meta->model = NULL;
meta->empty = false;
+ meta->has_model = false;
+ for (int i = 0; i < 6; i++)
+ meta->depends[i] = false;
}
// callback for deleting a chunk
void client_terrain_chunk_received(TerrainChunk *chunk)
{
pthread_mutex_lock(&chunk->mtx);
- TerrainChunkMeta *extra = chunk->extra;
- if (extra->state == CHUNK_RECIEVING)
- extra->state = CHUNK_FRESH;
+ TerrainChunkMeta *meta = chunk->extra;
+
+ if (meta->state == CHUNK_RECIEVING)
+ meta->state = CHUNK_FRESH;
+
pthread_mutex_unlock(&chunk->mtx);
- client_terrain_meshgen_task(chunk);
+ client_terrain_meshgen_task(chunk, true);
- v3s32 neighbors[6] = {
- {+1, 0, 0},
- { 0, +1, 0},
- { 0, 0, +1},
- {-1, 0, 0},
- { 0, -1, 0},
- { 0, 0, -1},
- };
+ for (int i = 0; i < 6; i++) {
+ TerrainChunk *neighbor = terrain_get_chunk(client_terrain,
+ v3s32_sub(chunk->pos, facedir[i]), false);
+ if (!neighbor)
+ continue;
- for (int i = 0; i < 6; i++)
- client_terrain_meshgen_task(terrain_get_chunk(client_terrain,
- v3s32_add(chunk->pos, neighbors[i]), false));
+ pthread_mutex_lock(&neighbor->mtx);
+ TerrainChunkMeta *neighbor_meta = neighbor->extra;
+ if (neighbor_meta->depends[i])
+ client_terrain_meshgen_task(neighbor, true);
+ pthread_mutex_unlock(&neighbor->mtx);
+ }
}
// enqueue chunk to mesh update queue
-void client_terrain_meshgen_task(TerrainChunk *chunk)
+void client_terrain_meshgen_task(TerrainChunk *chunk, bool changed)
{
- if (!chunk)
+ TerrainChunkMeta *meta = chunk->extra;
+ if (meta->queue)
return;
- pthread_mutex_lock(&chunk->mtx);
+ if (meta->empty) {
+ meta->has_model = true;
- TerrainChunkMeta *meta = chunk->extra;
- if (!meta->queue) {
- if (meta->empty) {
- if (meta->model) {
- meta->model->flags.delete = 1;
- meta->model = NULL;
- }
- } else {
- meta->queue = true;
- queue_enq(&meshgen_tasks, chunk);
+ if (meta->model) {
+ meta->model->flags.delete = 1;
+ meta->model = NULL;
}
- }
+ } else {
+ meta->queue = true;
- pthread_mutex_unlock(&chunk->mtx);
+ if (meta->has_model && changed)
+ queue_ppd(&meshgen_tasks, chunk);
+ else
+ queue_enq(&meshgen_tasks, chunk);
+ }
}
u64 sync; // keep track of when a chunk was synced the last time (used to detect when a chunk got out of and then back into load distance)
Model *model; // generated by terrain_gfx
bool empty; // whether the chunk is all air
+ bool has_model; // whether the chunk got a model before
+ bool depends[6]; // neighbours it depends on
} TerrainChunkMeta;
extern Terrain *client_terrain;
-void client_terrain_init(); // called on startup
-void client_terrain_deinit(); // called on shutdown
-void client_terrain_set_load_distance(u32 dist); // update load distance
-u32 client_terrain_get_load_distance(); // return load distance
-void client_terrain_start(); // start meshgen and sync threads
-void client_terrain_stop(); // stop meshgen and sync threads
-void client_terrain_chunk_received(TerrainChunk *chunk); // called when a chunk was recieved from server
-void client_terrain_meshgen_task(TerrainChunk *chunk); // enqueue chunk to mesh update queue
+void client_terrain_init(); // called on startup
+void client_terrain_deinit(); // called on shutdown
+void client_terrain_set_load_distance(u32 dist); // update load distance
+u32 client_terrain_get_load_distance(); // return load distance
+void client_terrain_start(); // start meshgen and sync threads
+void client_terrain_stop(); // stop meshgen and sync threads
+void client_terrain_chunk_received(TerrainChunk *chunk); // called when a chunk was recieved from server
+void client_terrain_meshgen_task(TerrainChunk *chunk, bool changed); // enqueue chunk to mesh update queue
#endif
--- /dev/null
+#include "client/facedir.h"
+
+v3s32 facedir[6] = {
+ {+0, +0, -1},
+ {+0, +0, +1},
+ {-1, +0, +0},
+ {+1, +0, +0},
+ {+0, -1, +0},
+ {+0, +1, +0},
+};
--- /dev/null
+#ifndef _FACEDIR_H_
+#define _FACEDIR_H_
+
+#include "types.h"
+
+extern v3s32 facedir[];
+
+#endif // _FACEDIR_H_
#include <stdlib.h>
#include "client/camera.h"
#include "client/client.h"
+#include "client/client_config.h"
+#include "client/client_inventory.h"
#include "client/client_player.h"
#include "client/debug_menu.h"
#include "client/gui.h"
+#include "client/interact.h"
#include "client/input.h"
#include "client/screenshot.h"
#include "client/window.h"
pthread_rwlock_unlock(&entity->lock_pos_rot);
refcount_drp(&entity->rc);
}
+
+void input_click(bool left)
+{
+ if (client_config.swap_mouse_buttons)
+ left = !left;
+
+ interact_use(left);
+}
#ifndef _INPUT_H_
#define _INPUT_H_
+#include <stdbool.h>
#include "types.h"
void input_init();
void input_tick(f64 dtime);
void input_cursor(double current_x, double current_y);
+void input_click(bool left);
#endif // _INPUT_H_
#include <linmath.h/linmath.h>
#include <stdio.h>
#include "client/camera.h"
+#include "client/client.h"
+#include "client/client_item.h"
#include "client/client_node.h"
+#include "client/client_player.h"
#include "client/cube.h"
#include "client/debug_menu.h"
#include "client/frustum.h"
bool old_exists = interact_pointed.exists;
v3s32 old_pointed = interact_pointed.pos;
if ((interact_pointed.exists = raycast(
- (v3f64) {camera.eye [0], camera.eye [1], camera.eye [2]},
+ (v3f64) {camera.eye [0], camera.eye [1], camera.eye [2]},
(v3f64) {camera.front[0], camera.front[1], camera.front[2]},
- 5, &interact_pointed.pos, &interact_pointed.node))
+ 5, &interact_pointed.pos, &interact_pointed.node))
&& !v3s32_equals(interact_pointed.pos, old_pointed)) {
mat4x4_translate(model,
interact_pointed.pos.x, interact_pointed.pos.y, interact_pointed.pos.z);
}
if (old_exists && !interact_pointed.exists)
- debug_menu_changed(ENTRY_POINTED);
+ debug_menu_changed(ENTRY_POINTED);
}
void interact_render()
glUniformMatrix4fv(loc_MVP, 1, GL_FALSE, mvp[0]); GL_DEBUG
mesh_render(&selection_mesh);
}
+
+void interact_use(bool left)
+{
+ ClientEntity *entity = client_player_entity_local();
+ if (!entity)
+ return;
+
+ ClientPlayerData *data = entity->extra;
+ pthread_mutex_lock(&data->mtx_inv);
+
+ ItemStack *stack = left ? &data->inventory.left : &data->inventory.right;
+ if (client_item_defs[stack->type].use && client_item_defs[stack->type].use(stack))
+ dragonnet_peer_send_ToServerInteract(client, &(ToServerInteract) {
+ .left = left,
+ .pointed = interact_pointed.exists,
+ .pos = interact_pointed.pos,
+ });
+
+ pthread_mutex_unlock(&data->mtx_inv);
+ refcount_drp(&entity->rc);
+}
#ifndef _INTERACT_H_
#define _INTERACT_H_
+#include <stdbool.h>
+#include "node.h"
+#include "types.h"
+
extern struct InteractPointed {
bool exists;
v3s32 pos;
void interact_deinit();
void interact_tick();
void interact_render();
+void interact_use(bool left);
#endif // _INTERACT_H_
#include "client/client_node.h"
#include "client/client_terrain.h"
#include "client/cube.h"
+#include "client/facedir.h"
#include "client/frustum.h"
#include "client/gl_debug.h"
#include "client/light.h"
TerrainChunk *chunk;
v3s32 chunkp;
TerrainChunk *nbrs[6];
- bool tried_nbrs[6];
- bool cull_edges;
+ bool *tried_nbrs;
+ bool show_edges;
+ bool shown_edges;
} ChunkRenderData;
static VertexLayout terrain_vertex_layout = {
.size = sizeof(TerrainVertex),
};
-static v3s32 face_dir[6] = {
- {+0, +0, -1},
- {+0, +0, +1},
- {-1, +0, +0},
- {+1, +0, +0},
- {+0, -1, +0},
- {+0, +1, +0},
-};
-
static v3f32 center_offset = {
CHUNK_SIZE * 0.5f + 0.5f,
CHUNK_SIZE * 0.5f + 0.5f,
static LightShader light_shader;
static ModelShader model_shader;
-static inline bool cull_face(NodeType self, NodeType nbr)
+static inline bool show_face(NodeType self, NodeType nbr)
{
switch (client_node_defs[self].visibility) {
case VISIBILITY_CLIP:
- return false;
+ return true;
case VISIBILITY_BLEND:
- return nbr == NODE_UNLOADED
- || nbr == self;
+ return nbr != self;
case VISIBILITY_SOLID:
- return nbr == NODE_UNLOADED
- || client_node_defs[nbr].visibility == VISIBILITY_SOLID;
+ return nbr != NODE_UNLOADED && client_node_defs[nbr].visibility != VISIBILITY_SOLID;
default: // impossible
break;
args.pos = v3s32_add(offset, data->chunkp);
for (args.f = 0; args.f < 6; args.f++) {
- v3s32 nbr_offset = v3s32_add(offset, face_dir[args.f]);
+ v3s32 nbr_offset = v3s32_add(offset, facedir[args.f]);
TerrainChunk *nbr_chunk;
nbr_chunk = data->chunk;
} else if (!(nbr_chunk = data->nbrs[args.f]) && !data->tried_nbrs[args.f]) {
nbr_chunk = data->nbrs[args.f] = terrain_get_chunk(client_terrain,
- v3s32_add(data->chunk->pos, face_dir[args.f]), false);
+ v3s32_add(data->chunk->pos, facedir[args.f]), false);
data->tried_nbrs[args.f] = true;
}
[(nbr_offset.y + CHUNK_SIZE) % CHUNK_SIZE]
[(nbr_offset.z + CHUNK_SIZE) % CHUNK_SIZE].type;
- if (cull_face(args.node->type, nbr_node)) {
- // exception to culling rules: don't cull solid edge nodes, unless cull_edges
- if (data->cull_edges || nbr_chunk == data->chunk || def->visibility != VISIBILITY_SOLID)
- continue;
- } else {
+ bool show = show_face(args.node->type, nbr_node);
+
+ if (show)
data->visible = true;
- }
+ else if (data->show_edges && nbr_chunk != data->chunk && def->visibility == VISIBILITY_SOLID)
+ data->shown_edges = show = true;
+
+ if (!show)
+ continue;
ModelBatch *batch = data->batch;
static void animate_chunk_model(Model *model, f64 dtime)
{
- if ((model->root->scale.x += dtime * 2.0f) > 1.0f) {
+ bool finished = (model->root->scale.x += dtime * 2.0f) > 1.0f;
+ if (finished)
model->root->scale.x = 1.0f;
- client_terrain_meshgen_task(model->extra);
- }
model->root->scale.z
= model->root->scale.y
= model->root->scale.x;
model_node_transform(model->root);
+
+ if (finished) {
+ model->callbacks.step = NULL;
+
+ if (model->extra) {
+ TerrainChunk *chunk = model->extra;
+
+ pthread_mutex_lock(&chunk->mtx);
+ client_terrain_meshgen_task(chunk, false);
+ pthread_mutex_unlock(&chunk->mtx);
+ }
+ }
}
-static Model *create_chunk_model(TerrainChunk *chunk, bool animate)
+static Model *create_chunk_model(TerrainChunk *chunk, bool animate, bool *depends)
{
ChunkRenderData data = {
.visible = false,
.chunk = chunk,
.chunkp = v3s32_scale(chunk->pos, CHUNK_SIZE),
.nbrs = {NULL},
- .tried_nbrs = {false},
- .cull_edges = !animate,
+ .tried_nbrs = depends,
+ .show_edges = animate,
+ .shown_edges = false,
};
CHUNK_ITERATE
render_node(&data, (v3s32) {x, y, z});
- if (!data.batch->textures.siz && !data.batch_transparent->textures.siz) {
+ if (!data.visible || (!data.batch->textures.siz && !data.batch_transparent->textures.siz)) {
model_batch_free(data.batch);
model_batch_free(data.batch_transparent);
return NULL;
}
Model *model = model_create();
- model->extra = chunk;
+ if (data.shown_edges)
+ model->extra = chunk;
model->box = (aabb3f32) {
v3f32_sub((v3f32) {-1.0f, -1.0f, -1.0f}, center_offset),
v3f32_add((v3f32) {+1.0f, +1.0f, +1.0f}, center_offset)};
model_batch_free(data.batch_transparent);
}
+ for (int i = 0; i < 6; i++)
+ if (data.nbrs[i])
+ data.tried_nbrs[i] = true;
+
return model;
}
void terrain_gfx_make_chunk_model(TerrainChunk *chunk)
{
TerrainChunkMeta *meta = chunk->extra;
+ pthread_mutex_lock(&chunk->mtx);
- bool animate = true;
+ bool animate;
+ if (meta->model)
+ animate = meta->model->callbacks.step ? true : false;
+ else
+ animate = !meta->has_model;
- pthread_mutex_lock(&chunk->mtx);
- if (meta->model && meta->model->root->scale.x == 1.0f)
- animate = false;
pthread_mutex_unlock(&chunk->mtx);
- Model *model = create_chunk_model(chunk, animate);
+ bool depends[6] = {false};
+ Model *model = create_chunk_model(chunk, animate, depends);
pthread_mutex_lock(&chunk->mtx);
}
meta->model = model;
+ meta->has_model = true;
+
+ for (int i = 0; i < 6; i++)
+ meta->depends[i] = depends[i];
+
pthread_mutex_unlock(&chunk->mtx);
}
}
}
+static void mouse_button_callback(__attribute__((unused)) GLFWwindow *handle, int button, int action, __attribute__((unused)) int mods)
+{
+ if ((button == GLFW_MOUSE_BUTTON_RIGHT || button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS)
+ input_click(button == GLFW_MOUSE_BUTTON_LEFT);
+}
+
void window_enter_fullscreen()
{
window.fullscreen = true;
glfwSetFramebufferSizeCallback(window.handle, &framebuffer_size_callback);
glfwSetCursorPosCallback(window.handle, &cursor_pos_callback);
glfwSetWindowPosCallback(window.handle, &window_pos_callback);
+ glfwSetMouseButtonCallback(window.handle, &mouse_button_callback);
return true;
}
--- /dev/null
+#ifndef _DIG_H_
+#define _DIG_H_
+
+typedef enum {
+ DIG_NONE = 0x0,
+ DIG_STONE = 0x01,
+ DIG_WOOD = 0x02,
+ DIG_DIRT = 0x04,
+} DigClass;
+
+#endif // _DIG_H_
stack->count = 1;
stack->data = NULL;
- if (item_defs[stack->type].create)
- item_defs[stack->type].create(stack);
+ if (item_defs[stack->type].callbacks.create)
+ item_defs[stack->type].callbacks.create(stack);
}
void item_stack_destroy(ItemStack *stack)
{
- if (item_defs[stack->type].delete)
- item_defs[stack->type].delete(stack);
+ if (item_defs[stack->type].callbacks.delete)
+ item_defs[stack->type].callbacks.delete(stack);
if (stack->data) {
free(stack->data);
stack->data = item_defs[stack->type].data_size > 0 ?
malloc(item_defs[stack->type].data_size) : NULL;
- if (item_defs[stack->type].create)
- item_defs[stack->type].create(stack);
+ if (item_defs[stack->type].callbacks.create)
+ item_defs[stack->type].callbacks.create(stack);
- if (item_defs[stack->type].deserialize)
- item_defs[stack->type].deserialize(&buffer, stack->data);
+ if (item_defs[stack->type].callbacks.deserialize)
+ item_defs[stack->type].callbacks.deserialize(&buffer, stack->data);
}
void item_stack_serialize(ItemStack *stack, SerializedItemStack *serialized)
serialized->count = stack->count;
serialized->data = (Blob) {0, NULL};
- if (item_defs[stack->type].serialize)
- item_defs[stack->type].serialize(&serialized->data, stack->data);
+ if (item_defs[stack->type].callbacks.serialize)
+ item_defs[stack->type].callbacks.serialize(&serialized->data, stack->data);
}
void item_stack_deserialize(ItemStack *stack, SerializedItemStack *serialized)
if (type >= COUNT_ITEM)
type = ITEM_UNKNOWN;
-
+
item_stack_set(stack, type, serialized->count, serialized->data);
}
{
.stackable = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// none
{
.stackable = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// pickaxe
{
.stackable = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_STONE,
+ .callbacks = {NULL},
},
// axe
{
.stackable = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_WOOD,
+ .callbacks = {NULL},
},
};
#include <stdbool.h>
#include <stddef.h>
+#include "dig.h"
#include "types.h"
typedef enum {
typedef struct {
bool stackable;
size_t data_size;
- void (*create)(ItemStack *stack);
- void (*delete)(ItemStack *stack);
- void (*serialize)(Blob *buffer, void *data);
- void (*deserialize)(Blob *buffer, void *data);
+ DigClass dig_class;
+ struct {
+ void (*create)(ItemStack *stack);
+ void (*delete)(ItemStack *stack);
+ void (*serialize)(Blob *buffer, void *data);
+ void (*deserialize)(Blob *buffer, void *data);
+ } callbacks;
} ItemDef;
void item_stack_initialize(ItemStack *stack);
+#include "dig.h"
#include "node.h"
#include "terrain.h"
#include "types.h"
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// air
{
.solid = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// grass
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_DIRT,
+ .callbacks = {NULL},
},
// dirt
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_DIRT,
+ .callbacks = {NULL},
},
// stone
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_STONE,
+ .callbacks = {NULL},
},
// snow
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_DIRT,
+ .callbacks = {NULL},
},
// oak wood
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_WOOD,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// oak leaves
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_NONE,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// pine wood
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_WOOD,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// pine leaves
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_NONE,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// palm wood
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_WOOD,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// palm leaves
{
.solid = true,
.data_size = sizeof(ColorData),
- .create = NULL,
- .delete = NULL,
- .serialize = (void *) &ColorData_write,
- .deserialize = (void *) &ColorData_read,
+ .dig_class = DIG_NONE,
+ .callbacks = {
+ .create = NULL,
+ .delete = NULL,
+ .serialize = (void *) &ColorData_write,
+ .deserialize = (void *) &ColorData_read,
+ },
},
// sand
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_DIRT,
+ .callbacks = {NULL},
},
// water
{
.solid = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// lava
{
.solid = false,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_NONE,
+ .callbacks = {NULL},
},
// vulcanostone
{
.solid = true,
.data_size = 0,
- .create = NULL,
- .delete = NULL,
- .serialize = NULL,
- .deserialize = NULL,
+ .dig_class = DIG_STONE,
+ .callbacks = {NULL},
},
};
typedef struct {
bool solid;
size_t data_size;
- void (*create)(struct TerrainNode *node);
- void (*delete)(struct TerrainNode *node);
- void (*serialize)(Blob *buffer, void *data);
- void (*deserialize)(Blob *buffer, void *data);
+ unsigned long dig_class;
+ struct {
+ void (*create)(struct TerrainNode *node);
+ void (*delete)(struct TerrainNode *node);
+ void (*serialize)(Blob *buffer, void *data);
+ void (*deserialize)(Blob *buffer, void *data);
+ } callbacks;
} NodeDef;
extern NodeDef node_defs[];
#include "interrupt.h"
#include "server/database.h"
#include "server/server.h"
+#include "server/server_item.h"
#include "server/server_player.h"
#include "server/server_terrain.h"
pkt->name = NULL;
}
-// set a node on the map
-static void on_ToServerSetnode(__attribute__((unused)) DragonnetPeer *peer, ToServerSetnode *pkt)
+static void on_ToServerInteract(DragonnetPeer *peer, ToServerInteract *pkt)
{
- terrain_set_node(server_terrain, pkt->pos,
- terrain_node_create(pkt->node, (Blob) {0, NULL}),
- false, NULL);
+ ServerPlayer *player = peer->extra;
+ pthread_mutex_lock(&player->mtx_inv);
+
+ ItemStack *stack = pkt->left ? &player->inventory.left : &player->inventory.right;
+ if (server_item_defs[stack->type].use)
+ server_item_defs[stack->type].use(player, stack, pkt->pointed, pkt->pos);
+
+ pthread_mutex_unlock(&player->mtx_inv);
}
// update player's position
server->on_disconnect = &server_player_remove;
server->on_recv = &on_recv;
server->on_recv_type[DRAGONNET_TYPE_ToServerAuth ] = (void *) &on_ToServerAuth;
- server->on_recv_type[DRAGONNET_TYPE_ToServerSetnode ] = (void *) &on_ToServerSetnode;
+ server->on_recv_type[DRAGONNET_TYPE_ToServerInteract ] = (void *) &on_ToServerInteract;
server->on_recv_type[DRAGONNET_TYPE_ToServerPosRot ] = (void *) &on_ToServerPosRot;
server->on_recv_type[DRAGONNET_TYPE_ToServerRequestChunk] = (void *) &on_ToServerRequestChunk;
}
};
-#define NUM_CONFIG_ENTRIES 6
-static ConfigEntry config_entries[NUM_CONFIG_ENTRIES] = {
+static ConfigEntry config_entries[] = {
{
.type = CONFIG_UINT,
.key = "load_distance",
__attribute__((constructor)) static void server_config_init()
{
- config_read("server.conf", config_entries, NUM_CONFIG_ENTRIES);
+ config_read("server.conf", config_entries, sizeof config_entries / sizeof *config_entries);
}
__attribute__((destructor)) static void server_config_deinit()
{
- config_free(config_entries, NUM_CONFIG_ENTRIES);
+ config_free(config_entries, sizeof config_entries / sizeof *config_entries);
}
--- /dev/null
+#include "node.h"
+#include "server/server_item.h"
+#include "server/server_terrain.h"
+
+static void use_dig(__attribute__((unused)) ServerPlayer *player, ItemStack *stack, bool pointed, v3s32 pos)
+{
+ if (!pointed)
+ return;
+
+ NodeType node = terrain_get_node(server_terrain, pos).type;
+
+ if (node == NODE_UNLOADED)
+ return;
+
+ if (!(node_defs[node].dig_class & item_defs[stack->type].dig_class))
+ return;
+
+ terrain_set_node(server_terrain, pos,
+ terrain_node_create(NODE_AIR, (Blob) {0, NULL}),
+ false, NULL);
+}
+
+ServerItemDef server_item_defs[COUNT_ITEM] = {
+ // unknown
+ {
+ .use = NULL,
+ },
+ // none
+ {
+ .use = NULL,
+ },
+ // pickaxe
+ {
+ .use = &use_dig,
+ },
+ // axe
+ {
+ .use = &use_dig,
+ },
+};
--- /dev/null
+#ifndef _SERVER_ITEM_H_
+#define _SERVER_ITEM_H_
+
+#include <stdbool.h>
+#include "item.h"
+#include "server/server_player.h"
+#include "types.h"
+
+typedef struct {
+ void (*use)(ServerPlayer *player, ItemStack *stack, bool pointed, v3s32 pos);
+} ServerItemDef;
+
+extern ServerItemDef server_item_defs[];
+
+#endif // _SERVER_ITEM_H_
static void send_player_inventory_existing(ServerPlayer *player, ServerPlayer *client)
{
if (client != player) {
- pthread_rwlock_rdlock(&player->lock_inv);
+ pthread_mutex_lock(&player->mtx_inv);
send_player_inventory(client, player);
- pthread_rwlock_unlock(&player->lock_inv);
- }
+ pthread_mutex_unlock(&player->mtx_inv);
+ }
}
// main thread
item_stack_destroy(&player->inventory.left);
item_stack_destroy(&player->inventory.right);
- pthread_rwlock_destroy(&player->lock_inv);
+ pthread_mutex_destroy(&player->mtx_inv);
free(player);
}
item_stack_initialize(&player->inventory.left);
item_stack_initialize(&player->inventory.right);
- pthread_rwlock_init(&player->lock_inv, NULL);
+ pthread_mutex_init(&player->mtx_inv, NULL);
printf("[access] connected %s\n", player->name);
peer->extra = refcount_grb(&player->rc);
map_trv(&players_named, func, arg, &refcount_obj, TRAVERSION_INORDER);
}
+void server_player_inventory_changed(ServerPlayer *player)
+{
+ server_player_iterate(&send_player_inventory, player);
+}
+
/*
229779
373875
ItemStack left;
ItemStack right;
} inventory;
- pthread_rwlock_t lock_inv;
+ pthread_mutex_t mtx_inv;
} ServerPlayer;
void server_player_init();
void server_player_disconnect(ServerPlayer *player);
void server_player_move(ServerPlayer *player, v3f64 pos, v3f32 rot);
void server_player_iterate(void *func, void *arg);
+void server_player_inventory_changed(ServerPlayer *player);
#endif // _SERVER_PLAYER_H_
NodeDef *def = &node_defs[node->type];
- if (def->serialize)
- def->serialize(&node_data->data, node->data);
+ if (def->callbacks.serialize)
+ def->callbacks.serialize(&node_data->data, node->data);
}
Blob buffer = {0, NULL};
node.type = type;
node.data = def->data_size ? malloc(def->data_size) : NULL;
- if (def->create)
- def->create(&node);
+ if (def->callbacks.create)
+ def->callbacks.create(&node);
- if (def->deserialize)
- def->deserialize(&buffer, node.data);
+ if (def->callbacks.deserialize)
+ def->callbacks.deserialize(&buffer, node.data);
return node;
}
{
NodeDef *def = &node_defs[node.type];
- if (def->delete)
- def->delete(&node);
+ if (def->callbacks.delete)
+ def->callbacks.delete(&node);
if (node.data)
free(node.data);
pkt ToServerAuth
String name
-pkt ToServerSetnode
+pkt ToServerInteract
+ u8 left
+ u8 pointed
v3s32 pos
- u32 node
pkt ToServerPosRot
v3f64 pos