瀏覽代碼

Update config

Add templates for config item types.

Also updated server's config to use the global config module.
feature/data-structs
TheoryOfNekomata 2 年之前
父節點
當前提交
57e4a768dc
共有 23 個檔案被更改,包括 670 行新增458 行删除
  1. +1
    -1
      CMakeLists.txt
  2. +63
    -7
      docs/reference/files/config-game.ini
  3. +137
    -159
      src/packages/config/IZ_config.c
  4. +24
    -2
      src/packages/config/IZ_config.h
  5. +2
    -2
      src/packages/game/IZ_app_video.c
  6. +2
    -2
      src/packages/game/input/IZ_action.h
  7. +3
    -3
      src/packages/game/input/IZ_input.c
  8. +76
    -54
      src/packages/game/input/IZ_joystick.c
  9. +4
    -1
      src/packages/game/input/IZ_joystick.h
  10. +55
    -35
      src/packages/game/input/IZ_keyboard.c
  11. +4
    -1
      src/packages/game/input/IZ_keyboard.h
  12. +94
    -61
      src/packages/game/input/IZ_midi.c
  13. +5
    -2
      src/packages/game/input/IZ_midi.h
  14. +23
    -23
      src/packages/game/input/input.test.c
  15. +14
    -3
      src/packages/game/output/video/IZ_video.c
  16. +1
    -1
      src/packages/game/util/IZ_midi.c
  17. +1
    -1
      src/packages/game/util/IZ_midi.h
  18. +77
    -44
      src/packages/net/IZ_net_client.c
  19. +7
    -2
      src/packages/net/IZ_net_client.h
  20. +66
    -43
      src/packages/net/IZ_net_server.c
  21. +3
    -3
      src/packages/net/IZ_net_server.h
  22. +6
    -6
      src/packages/server/IZ_app.c
  23. +2
    -2
      tools/websocket-client/index.html

+ 1
- 1
CMakeLists.txt 查看文件

@@ -17,7 +17,7 @@ add_definitions(
-DIZ_APP_NAME="Izanagi"
-DIZ_APP_DESCRIPTION="Run and gun game"
-DIZ_APP_SERVER_DESCRIPTION="Dedicated server"
-DIZ_PLAYERS=1
-DIZ_PLAYERS=2
)

if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")


+ 63
- 7
docs/reference/files/config-game.ini 查看文件

@@ -2,6 +2,9 @@
Width=320
Height=240
MaxFps=30
[Joystick.0]
DeviceID=0
AxisThreshold=8000
[Joystick.0.ControlMapping]
Affirm=11
Negate=10
@@ -15,9 +18,6 @@ Action6=8
Action7=9
Action8=13
Action9=14
[Joystick.0]
DeviceID=0
AxisThreshold=8000
[Keyboard.0.ControlMapping]
Up=Up
Right=Right
@@ -35,6 +35,9 @@ Action6=C
Action7=V
Action8=W
Action9=E
[MIDIInput.0]
DeviceID=0
Channel=0
[MIDIInput.0.ControlMapping]
Up="A#5"
Right=C6
@@ -52,11 +55,64 @@ Action6="F#4"
Action7=G4
Action8="G#4"
Action9=A4
[MIDIInput.0]
Channel=0
DeviceID=0
[Network]
Username=Player
PacketIntervalMs=200
MaxReconnectRetries=3
ReconnectIntervalSeconds=3
[Network.0]
Username=Player 1
[Joystick.1]
DeviceID=1
AxisThreshold=8000
[Joystick.1.ControlMapping]
Affirm=11
Negate=10
Action0=1
Action1=0
Action2=4
Action3=3
Action4=6
Action5=7
Action6=8
Action7=9
Action8=13
Action9=14
[Keyboard.1.ControlMapping]
Up=Keypad 8
Right=Keypad 6
Down=Keypad 5
Left=Keypad 4
Affirm=Keypad Enter
Negate=Keypad -
Action0=J
Action1=K
Action2=L
Action3=";"
Action4=M
Action5=,
Action6=.
Action7=/
Action8=I
Action9=O
[MIDIInput.1]
DeviceID=1
Channel=1
[MIDIInput.1.ControlMapping]
Up="A#5"
Right=C6
Down=B5
Left=A5
Affirm=F5
Negate=E5
Action0=C4
Action1="C#4"
Action2=D4
Action3="D#4"
Action4=E4
Action5=F4
Action6="F#4"
Action7=G4
Action8="G#4"
Action9=A4
[Network.1]
Username=Player 2

+ 137
- 159
src/packages/config/IZ_config.c 查看文件

@@ -23,75 +23,140 @@ const char* IZ_ConfigGetCommandlineOption(u8 argc, const char* argv[], const cha
return NULL;
}

typedef bool IZ_ConfigLoadParamsStringValidator(const char*);

typedef bool IZ_ConfigLoadParamsU16Validator(u16);
#define IZ_CONFIG_REGISTER_INT_TYPE(ID, T) \
typedef bool IZ_ConfigValidate##ID(T); \
\
typedef T IZ_ConfigDeserialize##ID(const char*); \
\
typedef void IZ_ConfigSerialize##ID(T, const char[128]); \
\
void IZ_ConfigEnsureValid##ID(IZ_ConfigItem* item, T raw_value, T default_value) { \
T* dest = item->dest; \
if (item->validator) { \
IZ_ConfigValidate##ID* validate = item->validator; \
if (validate(raw_value)) { \
*dest = raw_value; \
return; \
} \
*dest = default_value; \
return; \
} \
*dest = raw_value; \
} \
\
void IZ_ConfigLoad##ID(IZ_ConfigItem* item, const char* config_path) { \
static T raw_value; \
static T default_value; \
default_value = *((T*) item->default_value); \
if (item->transformer.deserialize && item->transformer.serialize) { \
IZ_ConfigDeserialize##ID* deserialize = item->transformer.deserialize; \
IZ_ConfigSerialize##ID* serialize = item->transformer.serialize; \
const char serialized_default_value[128]; \
if (default_value) { \
serialize(default_value, serialized_default_value); \
} \
char buffer[128]; \
ini_gets(item->section, item->key, serialized_default_value, buffer, 128, config_path); \
raw_value = deserialize(buffer); \
} else { \
raw_value = ini_getl(item->section, item->key, default_value, config_path); \
} \
IZ_ConfigEnsureValid##ID(item, raw_value, default_value); \
} \
\
IZ_ProcedureResult IZ_ConfigSave##ID(IZ_ConfigItem* item, const char* config_path) { \
T dest = *((T*) item->dest); \
if (item->validator) { \
IZ_ConfigValidate##ID* validate = item->validator; \
if (!validate(dest)) { \
dest = *((const T*) item->default_value); \
} \
} \
\
if (item->transformer.deserialize && item->transformer.serialize) { \
IZ_ConfigSerialize##ID* serialize = item->transformer.serialize; \
const char serialized_value[128]; \
serialize(dest, serialized_value); \
if (!ini_puts(item->section, item->key, serialized_value, config_path)) { \
return -1; \
} \
return 0; \
} \
\
if (!ini_putl(item->section, item->key, dest, config_path)) { \
return -1; \
} \
\
return 0; \
} \
\
void IZ_ConfigOverride##ID(IZ_ConfigItem* item, u8 argc, const char* argv[]) { \
if (!item->cmdline_option) { \
return; \
} \
const char* cmdline_buffer; \
char* rest_of_string; \
static T dest; \
static T config_value; \
config_value = *((T*) item->dest); \
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) { \
dest = strtol(cmdline_buffer, &rest_of_string, 10); \
if (strcmp(cmdline_buffer, rest_of_string) != 0) { \
IZ_ConfigEnsureValid##ID(item, dest, config_value); \
return; \
} \
} \
}

IZ_CONFIG_REGISTER_INT_TYPE(U8, u8);
IZ_CONFIG_REGISTER_INT_TYPE(U16, u16);
IZ_CONFIG_REGISTER_INT_TYPE(I32, i32);

typedef bool IZ_ConfigLoadParamsU8Validator(u8);
typedef bool IZ_ConfigLoadParamsStringValidator(const char*);

void IZ_ConfigEnsureValidString(IZ_ConfigItem item, const char* buffer) {
char dest[item.dest_size];
if (item.validator) {
IZ_ConfigLoadParamsStringValidator* validator = item.validator;
void IZ_ConfigEnsureValidString(IZ_ConfigItem* item, const char* buffer) {
if (item->validator) {
IZ_ConfigLoadParamsStringValidator* validator = item->validator;
if (validator(buffer)) {
memcpy_s(dest, item.dest_size, buffer, item.dest_size);
item.dest = &dest;
memcpy_s(item->dest, item->dest_size, buffer, item->dest_size);
return;
}
memcpy_s(dest, item.dest_size, item.default_value, item.dest_size);
item.dest = &dest;
memcpy_s(item->dest, item->dest_size, item->default_value, item->dest_size);
return;
}
memcpy_s(dest, item.dest_size, buffer, item.dest_size);
item.dest = &dest;
memcpy_s(item->dest, item->dest_size, buffer, item->dest_size);
}

void IZ_ConfigLoadString(IZ_ConfigItem item, const char* config_path) {
char buffer[item.dest_size];
ini_gets(item.section, item.key, item.default_value, buffer, (i32) item.dest_size, config_path);
void IZ_ConfigLoadString(IZ_ConfigItem* item, const char* config_path) {
char buffer[item->dest_size];
ini_gets(item->section, item->key, item->default_value, buffer, (i32) item->dest_size, config_path);
IZ_ConfigEnsureValidString(item, buffer);
}

void IZ_ConfigEnsureValidU8(IZ_ConfigItem item, u8 raw_value, u8 default_value) {
if (item.validator) {
IZ_ConfigLoadParamsU8Validator* validator = item.validator;
if (validator(raw_value)) {
item.dest = &raw_value;
return;
IZ_ProcedureResult IZ_ConfigSaveString(IZ_ConfigItem* item, const char* config_path) {
const char* dest = (const char*) item->dest;
if (item->validator) {
IZ_ConfigLoadParamsStringValidator* validator = item->validator;
if (!validator(dest)) {
dest = (const char*) item->default_value;
}
item.dest = &default_value;
return;
}
item.dest = &raw_value;
}

void IZ_ConfigLoadU8(IZ_ConfigItem item, const char* config_path) {
static u8 raw_value;
static u8 default_value;
default_value = *((u8*) item.default_value);
raw_value = ini_getl(item.section, item.key, default_value, config_path);
IZ_ConfigEnsureValidU8(item, raw_value, default_value);
if (!ini_puts(item->section, item->key, dest, config_path)) {
return -1;
}

return 0;
}

void IZ_ConfigEnsureValidU16(IZ_ConfigItem item, u16 raw_value, u16 default_value) {
if (item.validator) {
IZ_ConfigLoadParamsU16Validator* validator = item.validator;
if (validator(raw_value)) {
item.dest = &raw_value;
return;
}
item.dest = &default_value;
void IZ_ConfigOverrideString(IZ_ConfigItem* item, u8 argc, const char* argv[]) {
if (!item->cmdline_option) {
return;
}
item.dest = &raw_value;
}

void IZ_ConfigLoadU16(IZ_ConfigItem item, const char* config_path) {
static u16 raw_value;
static u16 default_value;
default_value = *((u16*) item.default_value);
raw_value = ini_getl(item.section, item.key, default_value, config_path);
IZ_ConfigEnsureValidU16(item, raw_value, default_value);
const char* cmdline_buffer;
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) {
IZ_ConfigEnsureValidString(item, cmdline_buffer);
}
}

void IZ_ConfigLoad(IZ_ConfigItem item[], const char* config_path) {
@@ -99,82 +164,38 @@ void IZ_ConfigLoad(IZ_ConfigItem item[], const char* config_path) {
for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) {
switch (item[i].type) {
case IZ_CONFIG_TYPE_STRING:
IZ_ConfigLoadString(item[i], config_path);
IZ_ConfigLoadString(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_U8:
IZ_ConfigLoadU8(item[i], config_path);
IZ_ConfigLoadU8(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_U16:
IZ_ConfigLoadU16(item[i], config_path);
IZ_ConfigLoadU16(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_I32:
IZ_ConfigLoadI32(&item[i], config_path);
default:
break;
}
}
}

IZ_ProcedureResult IZ_ConfigSaveU8(IZ_ConfigItem item, const char* config_path) {
u8 dest = *((u8*) item.dest);
if (item.validator) {
IZ_ConfigLoadParamsU8Validator* validator = item.validator;
if (!validator(dest)) {
dest = *((const u8*) item.default_value);
}
}

if (!ini_putl(item.section, item.key, dest, config_path)) {
return -1;
}

return 0;
}

IZ_ProcedureResult IZ_ConfigSaveU16(IZ_ConfigItem item, const char* config_path) {
u16 dest = *((u16*) item.dest);
if (item.validator) {
IZ_ConfigLoadParamsU8Validator* validator = item.validator;
if (!validator(dest)) {
dest = *((const u16*) item.default_value);
}
}

if (!ini_putl(item.section, item.key, dest, config_path)) {
return -1;
}

return 0;
}

IZ_ProcedureResult IZ_ConfigSaveString(IZ_ConfigItem item, const char* config_path) {
const char* dest = (const char*) item.dest;
if (item.validator) {
IZ_ConfigLoadParamsStringValidator* validator = item.validator;
if (!validator(dest)) {
dest = (const char*) item.default_value;
}
}

if (!ini_puts(item.section, item.key, dest, config_path)) {
return -1;
}

return 0;
}

IZ_ProcedureResult IZ_ConfigSave(IZ_ConfigItem item[], const char* config_path) {
u8 i;
u16 problems = 0;
for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) {
switch (item[i].type) {
case IZ_CONFIG_TYPE_STRING:
IZ_ConfigSaveString(item[i], config_path);
IZ_ConfigSaveString(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_U8:
IZ_ConfigSaveU8(item[i], config_path);
IZ_ConfigSaveU8(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_U16:
IZ_ConfigSaveU16(item[i], config_path);
IZ_ConfigSaveU16(&item[i], config_path);
break;
case IZ_CONFIG_TYPE_I32:
IZ_ConfigSaveI32(&item[i], config_path);
default:
break;
}
@@ -183,66 +204,21 @@ IZ_ProcedureResult IZ_ConfigSave(IZ_ConfigItem item[], const char* config_path)
return -problems;
}

void IZ_ConfigOverrideU8(IZ_ConfigItem item, u8 argc, const char* argv[]) {
if (!item.cmdline_option) {
return;
}
const char* cmdline_buffer;
char* rest_of_string;
static u8 dest;
static u8 default_value;
default_value = *((u16*) item.default_value);
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) {
dest = strtol(cmdline_buffer, &rest_of_string, 10);
if (strcmp(cmdline_buffer, rest_of_string) != 0) {
IZ_ConfigEnsureValidU8(item, dest, default_value);
return;
}
item.dest = &default_value;
}
}

void IZ_ConfigOverrideU16(IZ_ConfigItem item, u8 argc, const char* argv[]) {
if (!item.cmdline_option) {
return;
}
const char* cmdline_buffer;
char* rest_of_string;
static u16 dest;
static u16 default_value;
default_value = *((u16*) item.default_value);
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) {
dest = strtol(cmdline_buffer, &rest_of_string, 10);
if (strcmp(cmdline_buffer, rest_of_string) != 0) {
IZ_ConfigEnsureValidU16(item, dest, default_value);
return;
}
item.dest = &default_value;
}
}

void IZ_ConfigOverrideString(IZ_ConfigItem item, u8 argc, const char* argv[]) {
if (!item.cmdline_option) {
return;
}
const char* cmdline_buffer;
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) {
IZ_ConfigEnsureValidString(item, cmdline_buffer);
}
}

void IZ_ConfigOverride(IZ_ConfigItem item[], u8 argc, const char* argv[]) {
u8 i;
for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) {
switch (item[i].type) {
case IZ_CONFIG_TYPE_STRING:
IZ_ConfigOverrideString(&item[i], argc, argv);
break;
case IZ_CONFIG_TYPE_U8:
IZ_ConfigOverrideU8(item[i], argc, argv);
IZ_ConfigOverrideU8(&item[i], argc, argv);
break;
case IZ_CONFIG_TYPE_U16:
IZ_ConfigOverrideU16(item[i], argc, argv);
IZ_ConfigOverrideU16(&item[i], argc, argv);
break;
case IZ_CONFIG_TYPE_STRING:
IZ_ConfigOverrideString(item[i], argc, argv);
case IZ_CONFIG_TYPE_I32:
IZ_ConfigOverrideI32(&item[i], argc, argv);
break;
default:
break;
@@ -250,10 +226,12 @@ void IZ_ConfigOverride(IZ_ConfigItem item[], u8 argc, const char* argv[]) {
}
}

void IZ_ConfigInit(IZ_ConfigItem item[], const char* config_path, u8 argc, const char* argv[]) {
IZ_ProcedureResult IZ_ConfigInitialize(IZ_ConfigItem item[], const char* config_path, u8 argc, const char* argv[]) {
IZ_ConfigLoad(item, config_path);
if (IZ_ConfigSave(item, config_path)) {
// TODO log errors in saving
i32 save_result = IZ_ConfigSave(item, config_path);
if (save_result < 0) {
return -1;
}
IZ_ConfigOverride(item, argc, argv);
return save_result > 0 ? 1 : 0;
}

+ 24
- 2
src/packages/config/IZ_config.h 查看文件

@@ -10,9 +10,15 @@ typedef enum {
IZ_CONFIG_TYPE_VOID,
IZ_CONFIG_TYPE_STRING,
IZ_CONFIG_TYPE_U8,
IZ_CONFIG_TYPE_U16
IZ_CONFIG_TYPE_U16,
IZ_CONFIG_TYPE_I32,
} IZ_ConfigType;

typedef struct {
void* serialize;
void* deserialize;
} IZ_ConfigSerializerPair;

typedef struct {
IZ_ConfigType type;
size_t dest_size;
@@ -21,6 +27,7 @@ typedef struct {
const char* cmdline_option;
const void* default_value;
void* validator;
IZ_ConfigSerializerPair transformer;
void* dest;
} IZ_ConfigItem;

@@ -28,8 +35,23 @@ void IZ_ConfigGetDefaultPath(const char*, size_t);

const char* IZ_ConfigGetCommandlineOption(u8, const char*[], const char*);

void IZ_ConfigInit(IZ_ConfigItem[], const char*, u8, const char*[]);
IZ_ProcedureResult IZ_ConfigInitialize(IZ_ConfigItem[], const char*, u8, const char*[]);

IZ_ProcedureResult IZ_ConfigSave(IZ_ConfigItem[], const char*);

#define IZ_CONFIG_ITEM_NULL (IZ_ConfigItem) { \
IZ_CONFIG_TYPE_VOID, \
0, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
{ \
.serialize = NULL, \
.deserialize = NULL, \
}, \
NULL, \
}

#endif

+ 2
- 2
src/packages/game/IZ_app_video.c 查看文件

@@ -35,7 +35,7 @@ void IZ_VideoUpdateForDebugInput(IZ_VideoState* video_state, IZ_InputState* inpu
u8 control_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
IZ_Action the_action = input_state->action[player_index];
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
column = (control_index % 4) + (player_index * 4);
row = control_index / 4;

@@ -87,7 +87,7 @@ void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetClientState* ne
u8 control_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
IZ_Action the_action = net_state->action[player_index];
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
column = (control_index % 4) + (player_index * 4);
row = control_index / 4;



+ 2
- 2
src/packages/game/input/IZ_action.h 查看文件

@@ -3,11 +3,11 @@

#include "../../common/IZ_common.h"

#define CONTROLS (unsigned char) 16
#define IZ_CONTROLS (unsigned char) 16

typedef u16 IZ_Action;

static const char* ACTION_NAMES[CONTROLS] = {
static const char* IZ_ACTION_NAMES[IZ_CONTROLS] = {
"Up",
"Right",
"Down",


+ 3
- 3
src/packages/game/input/IZ_input.c 查看文件

@@ -19,15 +19,15 @@ IZ_ProcedureResult IZ_InputInitialize(IZ_InputState* state, const char* config_p

IZ_ProcedureResult result = 0;

if (IZ_JoystickInitialize(&state->joystick_state, config_path, argc, argv)) {
if (IZ_JoystickInitialize(&state->joystick_state, config_path, argc, argv) < 0) {
result |= 1;
}

if (IZ_KeyboardInitialize(&state->keyboard_state, config_path, argc, argv)) {
if (IZ_KeyboardInitialize(&state->keyboard_state, config_path, argc, argv) < 0) {
result |= 2;
}

if (IZ_MIDIInputInitialize(&state->midi_input_state, config_path, argc, argv)) {
if (IZ_MIDIInputInitialize(&state->midi_input_state, config_path, argc, argv) < 0) {
result |= 4;
}



+ 76
- 54
src/packages/game/input/IZ_joystick.c 查看文件

@@ -1,5 +1,11 @@
#include "IZ_joystick.h"

static IZ_ConfigItem joystick_config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 2) + 1];

bool IZ_JoystickIsValidAxisThreshold(u16 value) {
return (4000 <= value && value <= 12000);
}

void IZ_JoystickHandleDeviceEvents(IZ_JoystickState* state, SDL_Event e) {
if (e.type == SDL_JOYDEVICEADDED) {
if (SDL_NumJoysticks() <= IZ_PLAYERS && !state->device) {
@@ -66,7 +72,7 @@ void IZ_JoystickHandleHatEvents(IZ_Action* action, SDL_Event e) {

void IZ_JoystickHandleButtonEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) {
u8 control_index;
for (control_index = 4; control_index < CONTROLS; control_index += 1) {
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
if (e.jbutton.button == state->config.control_mapping[control_index]) {
const u16 bitflag = (0x1 << control_index);

@@ -93,84 +99,100 @@ void IZ_JoystickHandleEvents(IZ_JoystickState(* state)[IZ_PLAYERS], IZ_Action(*
}
}

void IZ_JoystickLoadConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path) {
char control_mapping_section_name[26];
char main_section_name[11];

void IZ_JoystickBindStateToConfig(IZ_JoystickState(* state)[IZ_PLAYERS], IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index);

for (control_index = 4; control_index < CONTROLS; control_index += 1) {
(*state)[player_index].config.control_mapping[control_index] = ini_getl(
control_mapping_section_name,
ACTION_NAMES[control_index],
IZ_JOYSTICK_DEFAULT_STATE[player_index].config.control_mapping[control_index],
config_path
);
u8 base_index = (player_index * (IZ_CONTROLS - 4 + 2));
config_items[base_index].dest = &((*state)[player_index].config.device_id);
config_items[base_index + 1].dest = &((*state)[player_index].config.axis_threshold);
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
config_items[base_index + 2 + (control_index - 4)].dest = &((*state)[player_index].config.control_mapping[control_index]);
}

sprintf_s(main_section_name, 11, "Joystick.%d", player_index);
(*state)[player_index].config.axis_threshold = ini_getl(main_section_name, "AxisThreshold", IZ_DEFAULT_AXIS_THRESHOLD, config_path);
(*state)[player_index].config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path);
}
}

IZ_ProcedureResult IZ_JoystickSaveConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path) {
u8 problem = 0;

char control_mapping_section_name[26];
char main_section_name[11];
IZ_JoystickBindStateToConfig(state, joystick_config_items);
return IZ_ConfigSave(joystick_config_items, config_path);
}

void IZ_JoystickInitializeConfigItems(IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;
char* main_section_name;
char* control_mapping_section_name;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index);
for (control_index = 4; control_index < CONTROLS; control_index += 1) {
if (!ini_putl(
control_mapping_section_name,
ACTION_NAMES[control_index],
(*state)[player_index].config.control_mapping[control_index],
config_path
)) {
problem |= (1 << player_index);
}
}

sprintf_s(main_section_name, 11, "Joystick.%d", player_index);
if (!ini_putl(
main_section_name = SDL_malloc(sizeof(char) * 64);
sprintf_s(main_section_name, 64, "Joystick.%d", player_index);
u8 base_index = (player_index * (IZ_CONTROLS - 4 + 2));
config_items[base_index] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_I32,
sizeof(i32),
main_section_name,
"DeviceID",
(*state)[player_index].config.device_id,
config_path
)) {
problem |= (1 << player_index);
}

if (!ini_putl(
NULL,
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.device_id,
NULL,
NULL,
};

config_items[base_index + 1] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U16,
sizeof(u16),
main_section_name,
"AxisThreshold",
(*state)[player_index].config.axis_threshold,
config_path
)) {
problem |= (1 << player_index);
NULL,
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.axis_threshold,
IZ_JoystickIsValidAxisThreshold,
NULL,
};

control_mapping_section_name = SDL_malloc(sizeof(char) * 64);
sprintf_s(control_mapping_section_name, 64, "Joystick.%d.ControlMapping", player_index);
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) {
config_items[base_index + 2 + (control_index - 4)] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U8,
sizeof(u8),
control_mapping_section_name,
IZ_ACTION_NAMES[control_index],
NULL,
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.control_mapping[control_index],
NULL,
NULL,
};
}
}

return -problem;
config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 2)] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_VOID,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
}

IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
SDL_memcpy(state, &IZ_JOYSTICK_DEFAULT_STATE, sizeof(IZ_JoystickState));

IZ_JoystickLoadConfig(state, config_path);
if (IZ_JoystickSaveConfig(state, config_path) < 0) {
IZ_ProcedureResult IZ_JoystickInitializeConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
IZ_JoystickInitializeConfigItems(joystick_config_items);
IZ_JoystickBindStateToConfig(state, joystick_config_items);
if (IZ_ConfigInitialize(joystick_config_items, config_path, argc, argv) < 0) {
return -1;
}
return 0;
}

IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
SDL_memcpy(state, &IZ_JOYSTICK_DEFAULT_STATE, sizeof(IZ_JoystickState) * IZ_PLAYERS);
if (IZ_JoystickInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}

u8 joysticks_count = SDL_NumJoysticks();
u8 player_index;
u8 joysticks_count = SDL_NumJoysticks();
for (player_index = 0; player_index < joysticks_count; player_index += 1) {
if (player_index >= IZ_PLAYERS) {
break;


+ 4
- 1
src/packages/game/input/IZ_joystick.h 查看文件

@@ -5,6 +5,7 @@
#include <SDL_joystick.h>
#include <SDL_events.h>
#include <minIni.h>
#include "../../config/IZ_config.h"
#include "IZ_action.h"

typedef u8 IZ_PadButton;
@@ -21,7 +22,7 @@ typedef enum {
typedef struct {
u16 axis_threshold;
SDL_JoystickID device_id;
IZ_PadButton control_mapping[CONTROLS];
IZ_PadButton control_mapping[IZ_CONTROLS];
} IZ_JoystickConfig;

typedef struct {
@@ -55,6 +56,7 @@ static const IZ_JoystickState IZ_JOYSTICK_DEFAULT_STATE[IZ_PLAYERS] = {
},
.device = NULL,
},
#if IZ_PLAYERS > 1
{
.config = {
.control_mapping = {
@@ -80,6 +82,7 @@ static const IZ_JoystickState IZ_JOYSTICK_DEFAULT_STATE[IZ_PLAYERS] = {
},
.device = NULL,
},
#endif
};

IZ_ProcedureResult IZ_JoystickSaveConfig(IZ_JoystickState(*)[IZ_PLAYERS], const char*);


+ 55
- 35
src/packages/game/input/IZ_keyboard.c 查看文件

@@ -1,8 +1,19 @@
#include "IZ_keyboard.h"

static IZ_ConfigItem keyboard_config_items[IZ_PLAYERS * IZ_CONTROLS + 1];

void IZ_KeyboardSerializeControl(i32 value, char control[128]) {
const char* serialized = SDL_GetKeyName(value);
memcpy_s(control, 128, serialized, 128);
}

i32 IZ_KeyboardDeserializeControl(const char* control) {
return SDL_GetKeyFromName(control);
}

void IZ_KeyboardHandleKeyUpDownEvents(IZ_KeyboardState* state, IZ_Action* action, SDL_Event e) {
u8 control_index;
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
if (e.key.keysym.sym == state->config.control_mapping[control_index]) {
const u16 bitflag = (0x1 << control_index);
if (e.type == SDL_KEYDOWN) {
@@ -23,54 +34,63 @@ void IZ_KeyboardHandleEvents(IZ_KeyboardState(* state)[IZ_PLAYERS], IZ_Action(*
}
}

IZ_ProcedureResult IZ_KeyboardSaveConfig(IZ_KeyboardState(* state)[IZ_PLAYERS], const char* config_path) {
u8 problem = 0;
char control_mapping_section_name[26];

void IZ_KeyboardBindStateToConfig(IZ_KeyboardState(* state)[IZ_PLAYERS], IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(control_mapping_section_name, 26, "Keyboard.%d.ControlMapping", player_index);
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
if (!ini_puts(
control_mapping_section_name,
ACTION_NAMES[control_index],
SDL_GetKeyName((*state)[player_index].config.control_mapping[control_index]),
config_path
)) {
problem |= (1 << player_index);
}
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
config_items[player_index * IZ_CONTROLS + control_index].dest = &((*state)[player_index].config.control_mapping[control_index]);
}
}

return problem;
}

void IZ_KeyboardLoadConfig(IZ_KeyboardState(* state)[IZ_PLAYERS], const char* config_path) {
char buffer[128];
char keyboard_section_name[26];
IZ_ProcedureResult IZ_KeyboardSaveConfig(IZ_KeyboardState(* state)[IZ_PLAYERS], const char* config_path) {
IZ_KeyboardBindStateToConfig(state, keyboard_config_items);
return IZ_ConfigSave(keyboard_config_items, config_path);
}

void IZ_KeyboardInitializeConfigItems(IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;
char* control_mapping_section_name;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index);
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
ini_gets(
keyboard_section_name,
ACTION_NAMES[control_index],
SDL_GetKeyName(IZ_KEYBOARD_DEFAULT_STATE[player_index].config.control_mapping[control_index]),
buffer,
128,
config_path
);

(*state)[player_index].config.control_mapping[control_index] = SDL_GetKeyFromName(buffer);
control_mapping_section_name = SDL_malloc(sizeof(char) * 64);
sprintf_s(control_mapping_section_name, 64, "Keyboard.%d.ControlMapping", player_index);
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
config_items[player_index * IZ_CONTROLS + control_index] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_I32,
sizeof(i32),
control_mapping_section_name,
IZ_ACTION_NAMES[control_index],
NULL,
&IZ_KEYBOARD_DEFAULT_STATE[player_index].config.control_mapping[control_index],
NULL,
{
.serialize = IZ_KeyboardSerializeControl,
.deserialize = IZ_KeyboardDeserializeControl,
},
NULL,
};
}
}

config_items[IZ_PLAYERS * IZ_CONTROLS] = IZ_CONFIG_ITEM_NULL;
}

IZ_ProcedureResult IZ_KeyboardInitializeConfig(IZ_KeyboardState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
IZ_KeyboardInitializeConfigItems(keyboard_config_items);
IZ_KeyboardBindStateToConfig(state, keyboard_config_items);
if (IZ_ConfigInitialize(keyboard_config_items, config_path, argc, argv) < 0) {
return -1;
}
return 0;
}

IZ_ProcedureResult IZ_KeyboardInitialize(IZ_KeyboardState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
SDL_memcpy(state, &IZ_KEYBOARD_DEFAULT_STATE, sizeof(IZ_KeyboardState));
IZ_KeyboardLoadConfig(state, config_path);
return IZ_KeyboardSaveConfig(state, config_path);
SDL_memcpy(state, &IZ_KEYBOARD_DEFAULT_STATE, sizeof(IZ_KeyboardState) * IZ_PLAYERS);
if (IZ_KeyboardInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}

return 0;
}

+ 4
- 1
src/packages/game/input/IZ_keyboard.h 查看文件

@@ -5,10 +5,11 @@
#include <SDL_keyboard.h>
#include <SDL_events.h>
#include <minIni.h>
#include "../../config/IZ_config.h"
#include "IZ_action.h"

typedef struct {
SDL_KeyCode control_mapping[CONTROLS];
SDL_KeyCode control_mapping[IZ_CONTROLS];
} IZ_KeyboardConfig;

typedef struct {
@@ -38,6 +39,7 @@ static const IZ_KeyboardState IZ_KEYBOARD_DEFAULT_STATE[IZ_PLAYERS] = {
},
},
},
#if IZ_PLAYERS > 1
{
.config = {
.control_mapping = {
@@ -60,6 +62,7 @@ static const IZ_KeyboardState IZ_KEYBOARD_DEFAULT_STATE[IZ_PLAYERS] = {
},
},
},
#endif
};

IZ_ProcedureResult IZ_KeyboardSaveConfig(IZ_KeyboardState(*)[IZ_PLAYERS], const char*);


+ 94
- 61
src/packages/game/input/IZ_midi.c 查看文件

@@ -1,5 +1,20 @@
#include "IZ_midi.h"

static IZ_ConfigItem midi_input_config_items[(IZ_PLAYERS * (IZ_CONTROLS + 2)) + 1];

bool IZ_MIDIInputIsValidChannel(u8 value) {
return (0 <= value && value <= 15);
}

void IZ_MIDIInputSerializeControl(i32 value, char* control[128]) {
const char* serialized = IZ_MIDIGetNoteName(value);
memcpy_s(control, 128, serialized, 128);
}

i32 IZ_MIDIInputDeserializeControl(const char* control) {
return IZ_MIDIGetNoteFromName(control);
}

void IZ_MIDIInputHandleNoteOnOffEvents(IZ_MIDIInputState* state, IZ_Action* action, PmEvent e) {
u32 message = e.message;
u8 status = message & 0xF0u;
@@ -8,7 +23,7 @@ void IZ_MIDIInputHandleNoteOnOffEvents(IZ_MIDIInputState* state, IZ_Action* acti
// u8 data2 = (message >> 16) & 0xFFu;

u8 control_index;
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
if (
data1 == state->config.control_mapping[control_index]
&& (
@@ -36,87 +51,105 @@ void IZ_MIDIInputHandleEvents(IZ_MIDIInputState(* state)[IZ_PLAYERS], IZ_Action(
}
}

IZ_ProcedureResult IZ_MIDIInputSaveConfig(IZ_MIDIInputState(* state)[IZ_PLAYERS], const char* config_path) {
u8 problem = 0;

char control_mapping_section_name[27];
char main_section_name[12];

void IZ_MIDIInputBindStateToConfig(IZ_MIDIInputState(* state)[IZ_PLAYERS], IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index);
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
if (!ini_puts(
control_mapping_section_name,
ACTION_NAMES[control_index],
IZ_MIDIGetNoteName((*state)[player_index].config.control_mapping[control_index]),
config_path
)) {
problem |= (1 << player_index);
}
}

sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index);
if (!ini_putl(
main_section_name,
"Channel",
(*state)[player_index].config.channel,
config_path
)) {
problem |= (1 << player_index);
}

if (!ini_putl(
main_section_name,
"DeviceID",
(*state)[player_index].config.device_id,
config_path
)) {
problem |= (1 << player_index);
u8 base_index = (player_index * (IZ_CONTROLS + 2));
config_items[base_index].dest = &((*state)[player_index].config.device_id);
config_items[base_index + 1].dest = &((*state)[player_index].config.channel);
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
config_items[base_index + 2 + control_index].dest = &((*state)[player_index].config.control_mapping[control_index]);
}
}

return problem;
}

void IZ_MIDIInputLoadConfig(IZ_MIDIInputState(* state)[IZ_PLAYERS], const char* config_path) {
char buffer[128];
char control_mapping_section_name[27];
char main_section_name[12];
IZ_ProcedureResult IZ_MIDIInputSaveConfig(IZ_MIDIInputState(* state)[IZ_PLAYERS], const char* config_path) {
IZ_MIDIInputBindStateToConfig(state, midi_input_config_items);
return IZ_ConfigSave(midi_input_config_items, config_path);
}

void IZ_MIDIInputInitializeConfigItems(IZ_ConfigItem config_items[]) {
u8 player_index;
u8 control_index;

char* main_section_name;
char* control_mapping_section_name;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index);
for (control_index = 0; control_index < CONTROLS; control_index += 1) {
ini_gets(
main_section_name = SDL_malloc(sizeof(char) * 64);
sprintf_s(main_section_name, 64, "MIDIInput.%d", player_index);

u16 base_index = (player_index * (IZ_CONTROLS + 2));
config_items[base_index] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_I32,
sizeof(i32),
main_section_name,
"DeviceID",
NULL,
&IZ_MIDI_INPUT_DEFAULT_STATE[player_index].config.device_id,
NULL,
{
.serialize = NULL,
.deserialize = NULL,
},
NULL,
};

config_items[base_index + 1] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U8,
sizeof(u8),
main_section_name,
"Channel",
NULL,
&IZ_MIDI_INPUT_DEFAULT_STATE[player_index].config.channel,
IZ_MIDIInputIsValidChannel,
{
.serialize = NULL,
.deserialize = NULL,
},
NULL,
};

control_mapping_section_name = SDL_malloc(sizeof(char) * 64);
sprintf_s(control_mapping_section_name, 64, "MIDIInput.%d.ControlMapping", player_index);
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) {
config_items[base_index + 2 + control_index] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U8,
sizeof(u8),
control_mapping_section_name,
ACTION_NAMES[control_index],
IZ_MIDIGetNoteName(IZ_MIDI_INPUT_DEFAULT_STATE[player_index].config.control_mapping[control_index]),
buffer,
128,
config_path
);

(*state)[player_index].config.control_mapping[control_index] = IZ_MIDIGetNoteFromName(buffer);
IZ_ACTION_NAMES[control_index],
NULL,
&IZ_MIDI_INPUT_DEFAULT_STATE[player_index].config.control_mapping[control_index],
NULL,
{
.serialize = IZ_MIDIInputSerializeControl,
.deserialize = IZ_MIDIInputDeserializeControl,
},
NULL,
};
}
}

sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index);
(*state)[player_index].config.channel = ini_getl(main_section_name, "Channel", player_index, config_path);
(*state)[player_index].config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path);
config_items[IZ_PLAYERS * (IZ_CONTROLS + 2)] = IZ_CONFIG_ITEM_NULL;
}

IZ_ProcedureResult IZ_MIDIInputInitializeConfig(IZ_MIDIInputState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
IZ_MIDIInputInitializeConfigItems(midi_input_config_items);
IZ_MIDIInputBindStateToConfig(state, midi_input_config_items);
if (IZ_ConfigInitialize(midi_input_config_items, config_path, argc, argv) < 0) {
return -1;
}
return 0;
}

IZ_ProcedureResult IZ_MIDIInputInitialize(IZ_MIDIInputState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) {
if (Pm_Initialize()) {
return 1;
return -1;
}

SDL_memcpy(state, &IZ_MIDI_INPUT_DEFAULT_STATE, sizeof(IZ_MIDIInputState));
IZ_MIDIInputLoadConfig(state, config_path);
if (IZ_MIDIInputSaveConfig(state, config_path)) {
return 2;
SDL_memcpy(state, &IZ_MIDI_INPUT_DEFAULT_STATE, sizeof(IZ_MIDIInputState) * IZ_PLAYERS);
if (IZ_MIDIInputInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}

u8 player_index;


+ 5
- 2
src/packages/game/input/IZ_midi.h 查看文件

@@ -10,15 +10,16 @@
#endif

#include <minIni.h>
#include "IZ_action.h"
#include "../../config/IZ_config.h"
#include "../util/IZ_midi.h"
#include "IZ_action.h"

#define MIDI_EVENT_BUFFER_SIZE 1024

typedef struct {
PmDeviceID device_id;
u8 channel;
IZ_MIDINote control_mapping[CONTROLS];
IZ_MIDINote control_mapping[IZ_CONTROLS];
} IZ_MIDIInputConfig;

typedef struct {
@@ -57,6 +58,7 @@ static const IZ_MIDIInputState IZ_MIDI_INPUT_DEFAULT_STATE[IZ_PLAYERS] = {
.stream = NULL,
.device_info = NULL,
},
#if IZ_PLAYERS > 1
{
.config = {
.control_mapping = {
@@ -84,6 +86,7 @@ static const IZ_MIDIInputState IZ_MIDI_INPUT_DEFAULT_STATE[IZ_PLAYERS] = {
.stream = NULL,
.device_info = NULL,
},
#endif
};

IZ_ProcedureResult IZ_MIDIInputSaveConfig(IZ_MIDIInputState(*)[IZ_PLAYERS], const char*);


+ 23
- 23
src/packages/game/input/input.test.c 查看文件

@@ -48,7 +48,7 @@ spec("input") {
}

it("calls load method") {
mock_set_expected_calls(ini_getl, ((CONTROLS - 4) + 2) * IZ_PLAYERS);
mock_set_expected_calls(ini_getl, ((IZ_CONTROLS - 4) + 2) * IZ_PLAYERS);

IZ_JoystickInitialize(&state, "config-game.ini", 0, NULL);

@@ -61,7 +61,7 @@ spec("input") {
}

it("calls save method") {
mock_set_expected_calls(ini_putl, ((CONTROLS - 4) + 2) * IZ_PLAYERS);
mock_set_expected_calls(ini_putl, ((IZ_CONTROLS - 4) + 2) * IZ_PLAYERS);

IZ_JoystickInitialize(&state, "config-game.ini", 0, NULL);

@@ -276,7 +276,7 @@ spec("input") {
}

for (u8 i = 0; i < 4; i += 1) {
it("handles motion for %s action", ACTION_NAMES[i]) {
it("handles motion for %s action", IZ_ACTION_NAMES[i]) {
e.jhat.value = (0x1u << i);
action[p] = 0;

@@ -287,7 +287,7 @@ spec("input") {
);
}

it("handles motion for %s deactivation", ACTION_NAMES[i]) {
it("handles motion for %s deactivation", IZ_ACTION_NAMES[i]) {
e.jhat.value = 0;
action[p] = ~0;

@@ -301,8 +301,8 @@ spec("input") {
}

describe("on button events") {
for (u8 i = 4; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
for (u8 i = 4; i < IZ_CONTROLS; i += 1) {
it("handles %s action activation", IZ_ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONDOWN;
e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i];
@@ -315,7 +315,7 @@ spec("input") {
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
it("handles %s action deactivation", IZ_ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONUP;
e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i];
@@ -342,14 +342,14 @@ spec("input") {

before_each() {
for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i];
}
}
}

it("calls save method") {
mock_set_expected_calls(ini_putl, ((CONTROLS - 4) + 2) * IZ_PLAYERS);
mock_set_expected_calls(ini_putl, ((IZ_CONTROLS - 4) + 2) * IZ_PLAYERS);

IZ_JoystickSaveConfig(&state, "config-game.ini");

@@ -409,7 +409,7 @@ spec("input") {

before_each() {
for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
}
}
@@ -422,7 +422,7 @@ spec("input") {
}

it("calls load method") {
mock_set_expected_calls(ini_gets, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_gets, IZ_CONTROLS * IZ_PLAYERS);

IZ_KeyboardInitialize(&state, "config-game.ini", 0, NULL);

@@ -435,7 +435,7 @@ spec("input") {
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_puts, IZ_CONTROLS * IZ_PLAYERS);

IZ_KeyboardInitialize(&state, "config-game.ini", 0, NULL);

@@ -455,8 +455,8 @@ spec("input") {

for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
describe("on player %u", p) {
for (u8 i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
it("handles %s action activation", IZ_ACTION_NAMES[i]) {
e.type = SDL_KEYDOWN;
e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
@@ -469,7 +469,7 @@ spec("input") {
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
it("handles %s action deactivation", IZ_ACTION_NAMES[i]) {
e.type = SDL_KEYUP;
e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
@@ -495,14 +495,14 @@ spec("input") {

before_each() {
for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
}
}
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_puts, IZ_CONTROLS * IZ_PLAYERS);

IZ_KeyboardSaveConfig("config-game.ini", &state);

@@ -556,7 +556,7 @@ spec("input") {
}

it("calls load method") {
mock_set_expected_calls(ini_gets, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_gets, IZ_CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_getl, 2 * IZ_PLAYERS);

IZ_MIDIInputInitialize(&state, "config-game.ini", 0, NULL);
@@ -577,7 +577,7 @@ spec("input") {
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_puts, IZ_CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_putl, 2 * IZ_PLAYERS);

IZ_MIDIInputInitialize(&state, "config-game.ini", 0, NULL);
@@ -623,7 +623,7 @@ spec("input") {
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_puts, IZ_CONTROLS * IZ_PLAYERS);
mock_set_expected_calls(ini_putl, 2 * IZ_PLAYERS);

IZ_MIDIInputSaveConfig("config-game.ini", &state);
@@ -651,8 +651,8 @@ spec("input") {

for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
describe("on player %u", p) {
for (u8 i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
it("handles %s action activation", IZ_ACTION_NAMES[i]) {
e.message = IZ_MIDI_NOTE_ON | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8);
state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i];
action[p] = 0;
@@ -664,7 +664,7 @@ spec("input") {
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
it("handles %s action deactivation", IZ_ACTION_NAMES[i]) {
e.message = IZ_MIDI_NOTE_OFF | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8);
state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i];
action[p] = ~0;


+ 14
- 3
src/packages/game/output/video/IZ_video.c 查看文件

@@ -42,6 +42,7 @@ static IZ_ConfigItem video_config_items[] = {
&IZ_VIDEO_DEFAULT_STATE.config.max_fps,
IZ_VideoIsValidMaxFPS,
},
IZ_CONFIG_ITEM_NULL,
};

void IZ_VideoBindStateToConfig(IZ_VideoState* state, IZ_ConfigItem config_items[]) {
@@ -55,10 +56,20 @@ IZ_ProcedureResult IZ_VideoSaveConfig(IZ_VideoState* state, const char* config_p
return IZ_ConfigSave(video_config_items, config_path);
}

IZ_ProcedureResult IZ_VideoInitializeConfig(IZ_VideoState* state, const char* config_path, u8 argc, const char* argv[]) {
IZ_VideoBindStateToConfig(state, video_config_items);
if (IZ_ConfigInitialize(video_config_items, config_path, argc, argv) < 0) {
return -1;
}
return 0;
}

IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, const char* config_path, u8 argc, const char* argv[]) {
SDL_memcpy(state, &IZ_VIDEO_DEFAULT_STATE, sizeof(IZ_VideoState));
IZ_VideoBindStateToConfig(state, video_config_items);
IZ_ConfigInit(video_config_items, config_path, argc, argv);
if (IZ_VideoInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}

state->last_update_at = 0u;
state->user_data = user_data;

@@ -72,7 +83,7 @@ IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, con
);
if (window == NULL) {
// fprintf_s(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
return -3;
}
state->window = window;
state->renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);


+ 1
- 1
src/packages/game/util/IZ_midi.c 查看文件

@@ -23,7 +23,7 @@ char* IZ_MIDIGetNoteName(u8 midi_note) {
return note_name;
}

u8 IZ_MIDIGetNoteFromName(char* name) {
u8 IZ_MIDIGetNoteFromName(const char* name) {
char name_copy[8];
memcpy_s(name_copy, 8, name, 8);
_strlwr_s(name_copy, 8);


+ 1
- 1
src/packages/game/util/IZ_midi.h 查看文件

@@ -13,6 +13,6 @@ static const u8 IZ_MIDI_NOTE_OFF = 0x80u;

char* IZ_MIDIGetNoteName(u8);

u8 IZ_MIDIGetNoteFromName(char*);
u8 IZ_MIDIGetNoteFromName(const char*);

#endif

+ 77
- 44
src/packages/net/IZ_net_client.c 查看文件

@@ -12,55 +12,87 @@ bool IZ_NetClientIsValidReconnectIntervalSeconds(long reconnect_interval_secs) {
return (3 <= reconnect_interval_secs && reconnect_interval_secs <= 10);
}

static IZ_ConfigItem net_config_items[] = {
{
IZ_CONFIG_TYPE_STRING,
32,
"Network",
"Username",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.username,
NULL
},
{
static IZ_ConfigItem net_client_config_items[IZ_PLAYERS + 3];

void IZ_NetClientInitializeConfigItems(IZ_ConfigItem config_items[]) {
config_items[0] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U16,
sizeof(u16),
"Network",
"PacketIntervalMs",
"-i",
&IZ_NET_CLIENT_DEFAULT_STATE.config.packet_interval_ms,
IZ_NetClientIsValidPacketIntervalMs,
},
{
sizeof(u16),
"Network",
"PacketIntervalMs",
"-i",
&IZ_NET_CLIENT_DEFAULT_STATE.config.packet_interval_ms,
IZ_NetClientIsValidPacketIntervalMs,
NULL,
};
config_items[1] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U8,
sizeof(u8),
"Network",
"MaxReconnectRetries",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.max_reconnect_retries,
IZ_NetClientIsValidMaxReconnectRetries,
},
{
sizeof(u8),
"Network",
"MaxReconnectRetries",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.max_reconnect_retries,
IZ_NetClientIsValidMaxReconnectRetries,
NULL,
};
config_items[2] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_U8,
sizeof(u8),
"Network",
"ReconnectIntervalSeconds",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.reconnect_interval_secs,
IZ_NetClientIsValidReconnectIntervalSeconds,
},
};
sizeof(u8),
"Network",
"ReconnectIntervalSeconds",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.reconnect_interval_secs,
IZ_NetClientIsValidReconnectIntervalSeconds,
NULL,
};

u8 player_index;
char* main_section_name;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
main_section_name = malloc(sizeof(char) * 64);
sprintf_s(main_section_name, 64, "Network.%d", player_index);
config_items[3 + player_index] = (IZ_ConfigItem) {
IZ_CONFIG_TYPE_STRING,
32,
main_section_name,
"Username",
NULL,
&IZ_NET_CLIENT_DEFAULT_STATE.config.usernames[player_index],
NULL,
NULL,
};
}

config_items[3 + IZ_PLAYERS] = IZ_CONFIG_ITEM_NULL;
}

void IZ_NetClientBindStateToConfig(IZ_NetClientState* state, IZ_ConfigItem config_items[]) {
config_items[0].dest = &state->config.username;
config_items[1].dest = &state->config.packet_interval_ms;
config_items[2].dest = &state->config.max_reconnect_retries;
config_items[3].dest = &state->config.reconnect_interval_secs;
config_items[0].dest = &state->config.packet_interval_ms;
config_items[1].dest = &state->config.max_reconnect_retries;
config_items[2].dest = &state->config.reconnect_interval_secs;
u8 player_index;
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) {
config_items[3 + player_index].dest = &state->config.usernames[player_index];
}
}

IZ_ProcedureResult IZ_NetClientSaveConfig(IZ_NetClientState* state, const char* config_path) {
IZ_NetClientBindStateToConfig(state, net_config_items);
return IZ_ConfigSave(net_config_items, config_path);
IZ_NetClientBindStateToConfig(state, net_client_config_items);
return IZ_ConfigSave(net_client_config_items, config_path);
}

IZ_ProcedureResult IZ_NetClientInitializeConfig(
IZ_NetClientState* state,
const char* config_path,
u8 argc,
const char* argv[]
) {
IZ_NetClientInitializeConfigItems(net_client_config_items);
IZ_NetClientBindStateToConfig(state, net_client_config_items);
if (IZ_ConfigInitialize(net_client_config_items, config_path, argc, argv) < 0) {
return -1;
}
return 0;
}

IZ_ProcedureResult IZ_NetClientInitialize(
@@ -71,10 +103,11 @@ IZ_ProcedureResult IZ_NetClientInitialize(
u8 argc,
const char* argv[]
) {
memcpy_s(state, sizeof(IZ_NetClientState), &IZ_NET_CLIENT_DEFAULT_STATE, sizeof(IZ_NetClientState));
IZ_NetClientBindStateToConfig(state, net_config_items);
IZ_ConfigInit(net_config_items, config_path, argc, argv);
if (!user_data) {
return -1;
}
memcpy_s(state, sizeof(IZ_NetClientState), &IZ_NET_CLIENT_DEFAULT_STATE, sizeof(IZ_NetClientState));
if (IZ_NetClientInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}
state->binding.user_data = user_data;


+ 7
- 2
src/packages/net/IZ_net_client.h 查看文件

@@ -19,7 +19,7 @@ typedef struct {
u16 packet_interval_ms;
u8 max_reconnect_retries;
u8 reconnect_interval_secs;
char username[32];
char usernames[IZ_PLAYERS][32];
} IZ_NetClientConfig;

typedef struct {
@@ -40,7 +40,12 @@ static IZ_NetClientState IZ_NET_CLIENT_DEFAULT_STATE = {
.packet_interval_ms = 200,
.max_reconnect_retries = 3,
.reconnect_interval_secs = 3,
.username = "Player",
.usernames = {
"Player 1",
#if IZ_PLAYERS > 1
"Player 2"
#endif
},
},
.binding = {
.interrupted = false,


+ 66
- 43
src/packages/net/IZ_net_server.c 查看文件

@@ -1,54 +1,79 @@
#include "IZ_net_server.h"

void IZ_NetLoadConfig(IZ_NetServerState* state, const char* config_path) {
char buffer[128];

ini_gets("Network", "Name", IZ_NET_SERVER_DEFAULT_STATE.config.name, buffer, 128, config_path);
memcpy_s(state->config.name, 64, buffer, 64);
bool IZ_NetServerIsValidPort(u16 port) {
return (1000 <= port && port <= 59999);
}

ini_gets("Network", "Motd", IZ_NET_SERVER_DEFAULT_STATE.config.motd, buffer, 128, config_path);
memcpy_s(state->config.motd, 128, buffer, 128);
static IZ_ConfigItem net_server_config_items[] = {
{
IZ_CONFIG_TYPE_STRING,
sizeof(char) * 64,
"Network",
"Name",
"-n",
&IZ_NET_SERVER_DEFAULT_STATE.config.name,
NULL,
{
.serialize = NULL,
.deserialize = NULL,
},
NULL,
},
{
IZ_CONFIG_TYPE_STRING,
sizeof(char) * 128,
"Network",
"Motd",
"-m",
&IZ_NET_SERVER_DEFAULT_STATE.config.motd,
NULL,
{
.serialize = NULL,
.deserialize = NULL,
},
NULL,
},
{
IZ_CONFIG_TYPE_U16,
sizeof(u16),
"Network",
"Port",
"-p",
&IZ_NET_SERVER_DEFAULT_STATE.config.port,
IZ_NetServerIsValidPort,
{
.serialize = NULL,
.deserialize = NULL,
},
NULL,
}
};

state->config.port = ini_getl("Network", "Port", IZ_NET_SERVER_DEFAULT_STATE.config.port, config_path);
void IZ_NetServerBindStateToConfig(IZ_NetServerState* state, IZ_ConfigItem config_items[]) {
config_items[0].dest = &state->config.name;
config_items[1].dest = &state->config.motd;
config_items[2].dest = &state->config.port;
}

IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetServerState* state, const char* config_path) {
if (!ini_puts("Network", "Name", state->config.name, config_path)) {
return -1;
}

if (!ini_puts("Network", "Motd", state->config.motd, config_path)) {
return -1;
}
IZ_NetServerBindStateToConfig(state, net_server_config_items);
return IZ_ConfigSave(net_server_config_items, config_path);
}

if (!ini_putl("Network", "Port", state->config.port, config_path)) {
IZ_ProcedureResult IZ_NetServerInitializeConfig(
IZ_NetServerState* state,
const char* config_path,
u8 argc,
const char* argv[]
) {
IZ_NetServerBindStateToConfig(state, net_server_config_items);
if (IZ_ConfigInitialize(net_server_config_items, config_path, argc, argv) < 0) {
return -1;
}

return 0;
}

void IZ_NetOverrideConfig(IZ_NetServerState* state, u8 argc, const char* argv[]) {
const char* cmdline_buffer;
char* rest_of_string;
u16 port;
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-p"))) {
port = strtol(cmdline_buffer, &rest_of_string, 10);
if (strcmp(cmdline_buffer, rest_of_string) != 0) {
state->config.port = port;
}
}

if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-n"))) {
memcpy_s(state->config.name, 64, cmdline_buffer, 64);
}

if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-m"))) {
memcpy_s(state->config.motd, 128, cmdline_buffer, 128);
}
}

IZ_ProcedureResult IZ_NetInitialize(
IZ_ProcedureResult IZ_NetServerInitialize(
IZ_NetServerState* state,
void* user_data,
const char* config_path,
@@ -59,15 +84,13 @@ IZ_ProcedureResult IZ_NetInitialize(
return -1;
}
memcpy_s(state, sizeof(IZ_NetServerState), &IZ_NET_SERVER_DEFAULT_STATE, sizeof(IZ_NetServerState));
IZ_NetLoadConfig(state, config_path);
if (IZ_NetSaveConfig(state, config_path) < 0) {
if (IZ_NetServerInitializeConfig(state, config_path, argc, argv) < 0) {
return -2;
}
IZ_NetOverrideConfig(state, argc, argv);
state->ws.user_data = user_data;
state->binding.user_data = user_data;
return 0;
}

void IZ_NetServerCancelService(IZ_NetServerState* state) {
IZ_WSServerCancelService(&state->ws);
IZ_WSServerCancelService(&state->binding);
}

+ 3
- 3
src/packages/net/IZ_net_server.h 查看文件

@@ -17,7 +17,7 @@ typedef struct {

typedef struct {
IZ_NetServerConfig config;
IZ_NetBinding ws;
IZ_NetBinding binding;
} IZ_NetServerState;

static IZ_NetServerState IZ_NET_SERVER_DEFAULT_STATE = {
@@ -26,7 +26,7 @@ static IZ_NetServerState IZ_NET_SERVER_DEFAULT_STATE = {
.name = IZ_APP_NAME " Server",
.motd = IZ_DEFAULT_MOTD,
},
.ws = {
.binding = {
.interrupted = false,
.context = NULL,
.connection = NULL,
@@ -34,7 +34,7 @@ static IZ_NetServerState IZ_NET_SERVER_DEFAULT_STATE = {
},
};

IZ_ProcedureResult IZ_NetInitialize(IZ_NetServerState*, void*, const char*, u8, const char*[]);
IZ_ProcedureResult IZ_NetServerInitialize(IZ_NetServerState *state, void *user_data, const char *config_path, u8 argc, const char **argv);

IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetServerState*, const char*);



+ 6
- 6
src/packages/server/IZ_app.c 查看文件

@@ -3,7 +3,7 @@
static IZ_App* global_app;

void IZ_AppHandleSignal(i32 _signal) {
global_app->net_state.ws.interrupted = true;
global_app->net_state.binding.interrupted = true;
IZ_NetServerCancelService(&global_app->net_state);
}

@@ -24,7 +24,7 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App *app, u8 argc, const char **argv) {
IZ_ConfigGetDefaultPath(config_path, 128);
}

if (IZ_NetInitialize(&app->net_state, app, config_path, argc, argv)) {
if (IZ_NetServerInitialize(&app->net_state, app, config_path, argc, argv)) {
return -1;
}

@@ -37,7 +37,7 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App *app, u8 argc, const char **argv) {

void IZ_AppTeardown(IZ_App* app) {
IZ_RepoTeardown(&app->repo_state);
IZ_WSServerTeardown(&app->net_state.ws);
IZ_WSServerTeardown(&app->net_state.binding);
lwsl_user("Server closed. Bye!\n");
}

@@ -87,7 +87,7 @@ IZ_ProcedureResult IZ_AppRun(IZ_App *app, u8 argc, const char **argv) {
return -1;
}

if (IZ_WSServerInitialize(&app->net_state.ws, (IZ_WSServerInitializeParams) {
if (IZ_WSServerInitialize(&app->net_state.binding, (IZ_WSServerInitializeParams) {
.port = app->net_state.config.port,
})) {
return -1;
@@ -95,12 +95,12 @@ IZ_ProcedureResult IZ_AppRun(IZ_App *app, u8 argc, const char **argv) {

i32 result = 0;
while (true) {
if (IZ_WSServerHandle(&app->net_state.ws)) {
if (IZ_WSServerHandle(&app->net_state.binding)) {
result = -1;
break;
}

if (app->net_state.ws.interrupted) {
if (app->net_state.binding.interrupted) {
break;
}
}


+ 2
- 2
tools/websocket-client/index.html 查看文件

@@ -78,7 +78,7 @@
type="url"
name="serverUrl"
autocomplete="off"
value="ws://localhost:42069"
value="binding://localhost:42069"
required
>
</label>
@@ -92,7 +92,7 @@
<span>Username</span>
<input
type="text"
name="username"
name="usernames"
autocomplete="off"
value="Web"
required


Loading…
取消
儲存