Restored console, has a bug
This commit is contained in:
@@ -30,6 +30,7 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||||
DUSK_SDL2
|
DUSK_SDL2
|
||||||
DUSK_OPENGL
|
DUSK_OPENGL
|
||||||
|
DUSK_CONSOLE_POSIX
|
||||||
# DUSK_OPENGL_LEGACY
|
# DUSK_OPENGL_LEGACY
|
||||||
DUSK_LINUX
|
DUSK_LINUX
|
||||||
DUSK_DISPLAY_SIZE_DYNAMIC
|
DUSK_DISPLAY_SIZE_DYNAMIC
|
||||||
|
|||||||
@@ -61,8 +61,9 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
|||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(log)
|
add_subdirectory(console)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
|
add_subdirectory(log)
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
@@ -77,4 +78,4 @@ add_subdirectory(time)
|
|||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(network)
|
add_subdirectory(network)
|
||||||
add_subdirectory(util)
|
add_subdirectory(util)
|
||||||
# add_subdirectory(thread)
|
add_subdirectory(thread)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
console.c
|
||||||
|
consolecmd.c
|
||||||
|
consolevar.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Subdirectories
|
||||||
|
add_subdirectory(cmd)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
void cmdAlias(const consolecmdexec_t *exec) {
|
||||||
|
if(exec->argc < 1) {
|
||||||
|
consolePrint("Expected 1 argument: <name> <command>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(exec->argc == 1 || strlen(exec->argv[1]) == 0) {
|
||||||
|
// Removing the alias.
|
||||||
|
for(uint32_t i = 0; i < CONSOLE.aliasCount; i++) {
|
||||||
|
if(stringCompare(CONSOLE.aliases[i].alias, exec->argv[0]) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move all the later aliases down one.
|
||||||
|
if(CONSOLE.aliasCount - i - 1 > 0) {
|
||||||
|
memoryMove(
|
||||||
|
&CONSOLE.aliases[i],
|
||||||
|
&CONSOLE.aliases[i + 1],
|
||||||
|
(CONSOLE.aliasCount - i - 1) * sizeof(consolealias_t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
CONSOLE.aliasCount--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias not found.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add alias
|
||||||
|
if(CONSOLE.aliasCount >= CONSOLE_ALIAS_MAX) {
|
||||||
|
consolePrint("Max aliases reached");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create alias
|
||||||
|
consolealias_t *alias = &CONSOLE.aliases[CONSOLE.aliasCount++];
|
||||||
|
stringCopy(alias->alias, exec->argv[0], CONSOLE_LINE_MAX);
|
||||||
|
stringCopy(alias->command, exec->argv[1], CONSOLE_LINE_MAX);
|
||||||
|
consolePrint("Added alias \"%s\" -> \"%s\"", exec->argv[0], exec->argv[1]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
|
||||||
|
void cmdBind(const consolecmdexec_t *exec) {
|
||||||
|
if(exec->argc < 1) {
|
||||||
|
consolePrint("Expected 1 argument: <key> <command]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(exec->argc == 1) {
|
||||||
|
consolePrint("TODO: Show binds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbutton_t button = inputButtonGetByName(exec->argv[0]);
|
||||||
|
if(button.type == INPUT_BUTTON_TYPE_NONE) {
|
||||||
|
consolePrint("Unknown button \"%s\"", exec->argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputBind(button, exec->argv[1]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void cmdEcho(const consolecmdexec_t *exec) {
|
||||||
|
if(exec->argc < 1) {
|
||||||
|
consolePrint("Expected 1 argument: <message>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolePrint("%s", exec->argv[0]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void cmdGet(const consolecmdexec_t *exec) {
|
||||||
|
assertTrue(
|
||||||
|
exec->argc >= 1,
|
||||||
|
"Get command requires 1 argument."
|
||||||
|
);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
||||||
|
consolevar_t *var = &CONSOLE.variables[i];
|
||||||
|
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
|
||||||
|
consolePrint("%s", var->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #include "console/cmd/cmdquit.h"
|
||||||
|
#include "console/cmd/cmdecho.h"
|
||||||
|
#include "console/cmd/cmdset.h"
|
||||||
|
#include "console/cmd/cmdget.h"
|
||||||
|
// #include "console/cmd/cmdbind.h"
|
||||||
|
// #include "console/cmd/cmdtoggleconsole.h"
|
||||||
|
// #include "console/cmd/cmdalias.h"
|
||||||
|
|
||||||
|
X(ECHO, "echo", cmdEcho)
|
||||||
|
X(SET, "set", cmdSet)
|
||||||
|
X(GET, "get", cmdGet)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "engine/engine.h"
|
||||||
|
|
||||||
|
void cmdQuit(const consolecmdexec_t *exec) {
|
||||||
|
ENGINE.running = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void cmdSet(const consolecmdexec_t *exec) {
|
||||||
|
assertTrue(exec->argc >= 2, "set command requires 2 arguments.");
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
||||||
|
consolevar_t *var = &CONSOLE.variables[i];
|
||||||
|
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
|
||||||
|
consoleVarSetValue(var, exec->argv[1]);
|
||||||
|
// consolePrint("%s %s", var->name, var->value);
|
||||||
|
for(i = 0; i < var->eventCount; i++) {
|
||||||
|
assertNotNull(var->events[i], "Event is NULL");
|
||||||
|
var->events[i](var);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void cmdToggleConsole(const consolecmdexec_t *exec) {
|
||||||
|
CONSOLE.visible = !CONSOLE.visible;
|
||||||
|
}
|
||||||
@@ -0,0 +1,455 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
#include "log/log.h"
|
||||||
|
#include "engine/engine.h"
|
||||||
|
#include "console/cmd/cmdset.h"
|
||||||
|
#include "console/cmd/cmdget.h"
|
||||||
|
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
|
||||||
|
console_t CONSOLE;
|
||||||
|
|
||||||
|
void consoleInit() {
|
||||||
|
memoryZero(&CONSOLE, sizeof(console_t));
|
||||||
|
|
||||||
|
// Register vars
|
||||||
|
consoleRegVar("fps", "0", NULL);
|
||||||
|
|
||||||
|
// Register cmds
|
||||||
|
CONSOLE.cmdGet = consoleRegCmd("get", cmdGet);
|
||||||
|
CONSOLE.cmdSet = consoleRegCmd("set", cmdSet);
|
||||||
|
|
||||||
|
#define X(unused, command, function) \
|
||||||
|
consoleRegCmd(command, function);
|
||||||
|
#include "console/cmd/cmdlist.h"
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadInit(&CONSOLE.thread, consoleInputThread);
|
||||||
|
threadMutexInit(&CONSOLE.execMutex);
|
||||||
|
threadStartRequest(&CONSOLE.thread);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
|
||||||
|
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
|
||||||
|
consoleCmdInit(cmd, name, function);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolevar_t * consoleRegVar(
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value,
|
||||||
|
consolevarchanged_t event
|
||||||
|
) {
|
||||||
|
consolevar_t *var;
|
||||||
|
|
||||||
|
// Existing?
|
||||||
|
var = consoleVarGet(name);
|
||||||
|
if(var != NULL) return var;
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
CONSOLE.variableCount < CONSOLE_VARIABLES_MAX,
|
||||||
|
"Too many console variables registered."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create
|
||||||
|
var = &CONSOLE.variables[CONSOLE.variableCount++];
|
||||||
|
consoleVarInitListener(var, name, value, event);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolevar_t * consoleVarGet(const char_t *name) {
|
||||||
|
assertNotNull(name, "name must not be NULL");
|
||||||
|
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
||||||
|
consolevar_t *var = &CONSOLE.variables[i];
|
||||||
|
if(stringCompare(var->name, name) == 0) return var;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void consolePrint(const char_t *message, ...) {
|
||||||
|
char_t buffer[CONSOLE_LINE_MAX];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, message);
|
||||||
|
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
// Move all lines back
|
||||||
|
memoryMove(
|
||||||
|
CONSOLE.line[0],
|
||||||
|
CONSOLE.line[1],
|
||||||
|
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy the new line
|
||||||
|
memoryCopy(
|
||||||
|
CONSOLE.line[CONSOLE_HISTORY_MAX - 1],
|
||||||
|
buffer,
|
||||||
|
len + 1
|
||||||
|
);
|
||||||
|
logDebug("%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleExec(const char_t *line) {
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assertNotNull(line, "line must not be NULL");
|
||||||
|
assertTrue(
|
||||||
|
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
|
||||||
|
"Too many commands in the buffer."
|
||||||
|
);
|
||||||
|
|
||||||
|
char_t buffer[CONSOLE_LINE_MAX];
|
||||||
|
size_t i = 0, j = 0;
|
||||||
|
char_t c;
|
||||||
|
consoleexecstate_t state = CONSOLE_EXEC_STATE_INITIAL;
|
||||||
|
consolecmdexec_t *exec = NULL;
|
||||||
|
|
||||||
|
while(state != CONSOLE_EXEC_STATE_FULLY_PARSED) {
|
||||||
|
c = line[i];
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case CONSOLE_EXEC_STATE_INITIAL:
|
||||||
|
assertTrue(j == 0, "Buffer not empty?");
|
||||||
|
|
||||||
|
if(c == '\0') {
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stringIsWhitespace(c) || c == ';') {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = CONSOLE_EXEC_STATE_PARSE_CMD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_PARSE_CMD:
|
||||||
|
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
|
||||||
|
state = CONSOLE_EXEC_STATE_CMD_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '"') {
|
||||||
|
// Can't handle quotes within the command.
|
||||||
|
consolePrint("Invalid command");
|
||||||
|
while(c != '\0' && c != ';') c = line[++i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[j++] = c;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if(j >= CONSOLE_LINE_MAX) {
|
||||||
|
consolePrint("Command too long");
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_CMD_PARSED:
|
||||||
|
if(j == 0) {
|
||||||
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create exec
|
||||||
|
assertNull(exec, "Existing command parsing?");
|
||||||
|
|
||||||
|
exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount];
|
||||||
|
memoryZero(exec, sizeof(consolecmdexec_t));
|
||||||
|
|
||||||
|
buffer[j] = '\0';
|
||||||
|
stringCopy(exec->command, buffer, CONSOLE_LINE_MAX);
|
||||||
|
state = CONSOLE_EXEC_STATE_FIND_ARG;
|
||||||
|
|
||||||
|
j = 0;// Free up buffer
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_FIND_ARG:
|
||||||
|
if(c == '\0' || c == ';') {
|
||||||
|
state = CONSOLE_EXEC_STATE_CMD_FINISHED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stringIsWhitespace(c)) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '"') {
|
||||||
|
state = CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED;
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
state = CONSOLE_EXEC_STATE_PARSE_ARG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_PARSE_ARG:
|
||||||
|
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
|
||||||
|
state = CONSOLE_EXEC_STATE_ARG_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[j++] = c;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if(j >= CONSOLE_LINE_MAX) {
|
||||||
|
consolePrint("Arg too long");
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED:
|
||||||
|
if(c == '"') {
|
||||||
|
state = CONSOLE_EXEC_STATE_ARG_PARSED;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '\0' || c == ';') {
|
||||||
|
consolePrint("Unterminated quote");
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '\\') {
|
||||||
|
c = line[++i];
|
||||||
|
|
||||||
|
if(c == '\0' || c == ';') {
|
||||||
|
consolePrint("Unterminated quote");
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[j++] = c;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if(j >= CONSOLE_LINE_MAX) {
|
||||||
|
consolePrint("Arg too long");
|
||||||
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_ARG_PARSED:
|
||||||
|
buffer[j] = '\0';
|
||||||
|
stringCopy(exec->argv[exec->argc++], buffer, CONSOLE_LINE_MAX);
|
||||||
|
state = CONSOLE_EXEC_STATE_FIND_ARG;
|
||||||
|
j = 0;// Free up buffer
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSOLE_EXEC_STATE_CMD_FINISHED:
|
||||||
|
assertNotNull(exec, "No command found?");
|
||||||
|
|
||||||
|
// Now, is there a command that matches?
|
||||||
|
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
|
||||||
|
consolecmd_t *cmd = &CONSOLE.commands[k];
|
||||||
|
if(stringCompare(cmd->name, exec->command) != 0) continue;
|
||||||
|
exec->cmd = cmd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(exec->cmd == NULL) {
|
||||||
|
// Command wasn't found, is there a variable that matches?
|
||||||
|
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
|
||||||
|
consolevar_t *var = &CONSOLE.variables[k];
|
||||||
|
if(stringCompare(var->name, exec->command) != 0) continue;
|
||||||
|
|
||||||
|
// Matching variable found, is this a GET or a SET?
|
||||||
|
if(exec->argc == 0) {
|
||||||
|
exec->cmd = CONSOLE.cmdGet;
|
||||||
|
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
|
||||||
|
exec->argc = 1;
|
||||||
|
} else {
|
||||||
|
exec->cmd = CONSOLE.cmdSet;
|
||||||
|
stringCopy(exec->argv[1], exec->argv[0], CONSOLE_LINE_MAX);
|
||||||
|
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
|
||||||
|
exec->argc = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable not found, is there an alias that matches?
|
||||||
|
bool_t aliasFound = false;
|
||||||
|
for(uint32_t k = 0; k < CONSOLE.aliasCount; k++) {
|
||||||
|
consolealias_t *alias = &CONSOLE.aliases[k];
|
||||||
|
if(stringCompare(alias->alias, exec->command) != 0) continue;
|
||||||
|
|
||||||
|
// Matching alias found, we unlock the mutex and recursively call
|
||||||
|
// consoleExec to handle the alias command.
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
consoleExec(alias->command);
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
aliasFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!aliasFound && exec->cmd == NULL) {
|
||||||
|
consolePrint("Command \"%s\" not found", exec->command);
|
||||||
|
exec = NULL;
|
||||||
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prep for next command.
|
||||||
|
exec = NULL;
|
||||||
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
||||||
|
CONSOLE.execBufferCount++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertUnreachable("Invalid state.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleUpdate() {
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Toggle console
|
||||||
|
// if(inputPressed(INPUT_ACTION_CONSOLE)) {
|
||||||
|
// CONSOLE.visible = !CONSOLE.visible;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Anything to exec?
|
||||||
|
if(CONSOLE.execBufferCount == 0) {
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the exec buffer, this allows exec command to work
|
||||||
|
consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX];
|
||||||
|
uint32_t execBufferCount = CONSOLE.execBufferCount;
|
||||||
|
memoryCopy(
|
||||||
|
execBuffer,
|
||||||
|
CONSOLE.execBuffer,
|
||||||
|
sizeof(consolecmdexec_t) * execBufferCount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear the exec buffer and unlock so new commands can be added while we
|
||||||
|
// process the current ones.
|
||||||
|
CONSOLE.execBufferCount = 0;
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Exec pending buffer.
|
||||||
|
for(uint32_t i = 0; i < execBufferCount; i++) {
|
||||||
|
consolecmdexec_t *exec = &execBuffer[i];
|
||||||
|
assertNotNull(exec->cmd, "Command execution has no command.");
|
||||||
|
exec->cmd->function(exec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t consoleDraw() {
|
||||||
|
if(!CONSOLE.visible) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorChain(shaderSetTexture(
|
||||||
|
&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &DEFAULT_FONT_TEXTURE
|
||||||
|
));
|
||||||
|
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
||||||
|
errorChain(textDraw(
|
||||||
|
32, 32,
|
||||||
|
"Hello World",
|
||||||
|
COLOR_WHITE,
|
||||||
|
&DEFAULT_FONT_TILESET,
|
||||||
|
&DEFAULT_FONT_TEXTURE
|
||||||
|
));
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleDispose(void) {
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadStop(&CONSOLE.thread);
|
||||||
|
threadMutexDispose(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
void consoleInputThread(thread_t *thread) {
|
||||||
|
assertNotNull(thread, "Thread cannot be NULL.");
|
||||||
|
|
||||||
|
char_t line[CONSOLE_LINE_MAX];
|
||||||
|
size_t cap = 0;
|
||||||
|
|
||||||
|
struct pollfd pfd = {
|
||||||
|
.fd = STDIN_FILENO,
|
||||||
|
.events = POLLIN
|
||||||
|
};
|
||||||
|
|
||||||
|
while(!threadShouldStop(thread) && ENGINE.running) {
|
||||||
|
int32_t rc = poll(&pfd, 1, CONSOLE_POSIX_POLL_RATE);
|
||||||
|
|
||||||
|
if(rc == 0) continue;
|
||||||
|
if(rc < 0) {
|
||||||
|
if(errno == EINTR) continue; // Interrupted by signal, retry
|
||||||
|
assertUnreachable("poll() failed with unexpected error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for errors or input
|
||||||
|
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) break;
|
||||||
|
if (!(pfd.revents & POLLIN)) {
|
||||||
|
pfd.revents = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a line from stdin
|
||||||
|
if(!fgets(line, CONSOLE_LINE_MAX, stdin)) {
|
||||||
|
if (feof(stdin)) break;
|
||||||
|
clearerr(stdin);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we read a full line or did it get truncated?
|
||||||
|
size_t len = strlen(line);
|
||||||
|
int32_t fullLine = len > 0 && line[len - 1] == '\n';
|
||||||
|
|
||||||
|
// Strip trailing newline/CR
|
||||||
|
while(len && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
|
||||||
|
line[--len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len > 0) consoleExec(line);
|
||||||
|
|
||||||
|
pfd.revents = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "consolevar.h"
|
||||||
|
#include "consolecmd.h"
|
||||||
|
#include "consolealias.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
#include "thread/thread.h"
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#define CONSOLE_POSIX_POLL_RATE 75
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONSOLE_EXEC_STATE_INITIAL,
|
||||||
|
CONSOLE_EXEC_STATE_PARSE_CMD,
|
||||||
|
CONSOLE_EXEC_STATE_CMD_PARSED,
|
||||||
|
|
||||||
|
CONSOLE_EXEC_STATE_FIND_ARG,
|
||||||
|
CONSOLE_EXEC_STATE_PARSE_ARG,
|
||||||
|
CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED,
|
||||||
|
CONSOLE_EXEC_STATE_ARG_PARSED,
|
||||||
|
|
||||||
|
CONSOLE_EXEC_STATE_CMD_FINISHED,
|
||||||
|
CONSOLE_EXEC_STATE_FULLY_PARSED
|
||||||
|
} consoleexecstate_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
consolecmd_t commands[CONSOLE_COMMANDS_MAX];
|
||||||
|
uint32_t commandCount;
|
||||||
|
|
||||||
|
consolevar_t variables[CONSOLE_VARIABLES_MAX];
|
||||||
|
uint32_t variableCount;
|
||||||
|
|
||||||
|
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
|
||||||
|
|
||||||
|
consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX];
|
||||||
|
uint32_t execBufferCount;
|
||||||
|
|
||||||
|
consolealias_t aliases[CONSOLE_ALIAS_MAX];
|
||||||
|
uint32_t aliasCount;
|
||||||
|
|
||||||
|
consolecmd_t *cmdGet;
|
||||||
|
consolecmd_t *cmdSet;
|
||||||
|
|
||||||
|
bool_t visible;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
char_t inputBuffer[CONSOLE_LINE_MAX];
|
||||||
|
thread_t thread;
|
||||||
|
threadmutex_t execMutex;
|
||||||
|
#endif
|
||||||
|
} console_t;
|
||||||
|
|
||||||
|
extern console_t CONSOLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the console.
|
||||||
|
*/
|
||||||
|
void consoleInit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a console command.
|
||||||
|
*
|
||||||
|
* @param name The name of the command.
|
||||||
|
* @param function The function to execute when the command is called.
|
||||||
|
* @return The registered command.
|
||||||
|
*/
|
||||||
|
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a console variable.
|
||||||
|
*
|
||||||
|
* @param name The name of the variable.
|
||||||
|
* @param value The initial value of the variable.
|
||||||
|
* @param event The event to register.
|
||||||
|
* @return The registered variable.
|
||||||
|
*/
|
||||||
|
consolevar_t * consoleRegVar(
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value,
|
||||||
|
consolevarchanged_t event
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a console variable by name.
|
||||||
|
*
|
||||||
|
* @param name The name of the variable.
|
||||||
|
* @return The variable, or NULL if not found.
|
||||||
|
*/
|
||||||
|
consolevar_t * consoleVarGet(const char_t *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a message to the console.
|
||||||
|
*
|
||||||
|
* @param message The message to print.
|
||||||
|
*/
|
||||||
|
void consolePrint(
|
||||||
|
const char_t *message,
|
||||||
|
...
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a console command. This method is thread safe and can be called from
|
||||||
|
* any thread.
|
||||||
|
*
|
||||||
|
* @param line The line to execute.
|
||||||
|
*/
|
||||||
|
void consoleExec(const char_t *line);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the console's pending commands.
|
||||||
|
*/
|
||||||
|
void consoleUpdate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the console to the screen. This is in UI space.
|
||||||
|
*
|
||||||
|
* @return The error return value.
|
||||||
|
*/
|
||||||
|
errorret_t consoleDraw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the console.
|
||||||
|
*/
|
||||||
|
void consoleDispose(void);
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
/**
|
||||||
|
* Input thread handler for posix input.
|
||||||
|
*
|
||||||
|
* @param thread The thread that is running.
|
||||||
|
*/
|
||||||
|
void consoleInputThread(thread_t *thread);
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "consolecmd.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t command[CONSOLE_LINE_MAX];
|
||||||
|
char_t alias[CONSOLE_LINE_MAX];
|
||||||
|
} consolealias_t;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "consolecmd.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
void consoleCmdInit(
|
||||||
|
consolecmd_t *cmd,
|
||||||
|
const char_t *name,
|
||||||
|
consolecmdfunc_t function
|
||||||
|
) {
|
||||||
|
assertNotNull(cmd, "Command is NULL.");
|
||||||
|
assertNotNull(name, "Name is NULL.");
|
||||||
|
assertNotNull(function, "Function is NULL.");
|
||||||
|
assertStrLenMin(name, 1, "Name is empty.");
|
||||||
|
assertStrLenMax(name, CONSOLE_CMD_NAME_MAX, "Name is too long.");
|
||||||
|
|
||||||
|
memoryZero(cmd, sizeof(consolecmd_t));
|
||||||
|
stringCopy(cmd->name, name, CONSOLE_CMD_NAME_MAX);
|
||||||
|
cmd->function = function;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "consoledefs.h"
|
||||||
|
|
||||||
|
typedef struct consolecmd_s consolecmd_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
consolecmd_t *cmd;
|
||||||
|
char_t command[CONSOLE_LINE_MAX];
|
||||||
|
char_t argv[CONSOLE_CMD_ARGC_MAX][CONSOLE_LINE_MAX];
|
||||||
|
uint32_t argc;
|
||||||
|
} consolecmdexec_t;
|
||||||
|
|
||||||
|
typedef void (*consolecmdfunc_t)(const consolecmdexec_t *exec);
|
||||||
|
|
||||||
|
typedef struct consolecmd_s {
|
||||||
|
char_t name[CONSOLE_CMD_NAME_MAX];
|
||||||
|
consolecmdfunc_t function;
|
||||||
|
} consolecmd_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a console command.
|
||||||
|
*
|
||||||
|
* @param cmd Pointer to the console command.
|
||||||
|
* @param name The name of the command.
|
||||||
|
* @param function The function to execute when the command is called.
|
||||||
|
*/
|
||||||
|
void consoleCmdInit(
|
||||||
|
consolecmd_t *cmd,
|
||||||
|
const char_t *name,
|
||||||
|
consolecmdfunc_t function
|
||||||
|
);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CONSOLE_CMD_NAME_MAX 32
|
||||||
|
#define CONSOLE_CMD_ARGC_MAX 16
|
||||||
|
|
||||||
|
#define CONSOLE_COMMANDS_MAX 32
|
||||||
|
#define CONSOLE_VARIABLES_MAX 64
|
||||||
|
#define CONSOLE_LINE_MAX 128
|
||||||
|
#define CONSOLE_HISTORY_MAX 16
|
||||||
|
#define CONSOLE_EXEC_BUFFER_MAX 32
|
||||||
|
#define CONSOLE_ALIAS_MAX 32
|
||||||
|
|
||||||
|
#define CONSOLE_VAR_NAME_MAX 32
|
||||||
|
#define CONSOLE_VAR_VALUE_MAX 128
|
||||||
|
#define CONSOLE_VAR_EVENTS_MAX 8
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "consolevar.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
void consoleVarInit(
|
||||||
|
consolevar_t *var,
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value
|
||||||
|
) {
|
||||||
|
assertNotNull(var, "var must not be NULL");
|
||||||
|
assertNotNull(name, "name must not be NULL");
|
||||||
|
assertNotNull(value, "value must not be NULL");
|
||||||
|
|
||||||
|
assertStrLenMin(name, 1, "name must not be empty");
|
||||||
|
assertStrLenMax(name, CONSOLE_VAR_NAME_MAX, "name is too long");
|
||||||
|
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
|
||||||
|
|
||||||
|
memoryZero(var, sizeof(consolevar_t));
|
||||||
|
stringCopy(var->name, name, CONSOLE_VAR_NAME_MAX);
|
||||||
|
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleVarInitListener(
|
||||||
|
consolevar_t *var,
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value,
|
||||||
|
consolevarchanged_t event
|
||||||
|
) {
|
||||||
|
consoleVarInit(var, name, value);
|
||||||
|
if(event) consoleVarListen(var, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleVarSetValue(consolevar_t *var, const char_t *value) {
|
||||||
|
assertNotNull(var, "var must not be NULL");
|
||||||
|
assertNotNull(value, "value must not be NULL");
|
||||||
|
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
|
||||||
|
|
||||||
|
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
|
||||||
|
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (i < var->eventCount) {
|
||||||
|
var->events[i](var);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleVarListen(consolevar_t *var, consolevarchanged_t event) {
|
||||||
|
assertNotNull(var, "var must not be NULL");
|
||||||
|
assertNotNull(event, "event must not be NULL");
|
||||||
|
assertTrue(
|
||||||
|
var->eventCount < CONSOLE_VAR_EVENTS_MAX,
|
||||||
|
"Event count is too high"
|
||||||
|
);
|
||||||
|
var->events[var->eventCount++] = event;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "consoledefs.h"
|
||||||
|
|
||||||
|
typedef struct consolevar_s consolevar_t;
|
||||||
|
|
||||||
|
typedef void (*consolevarchanged_t)(const consolevar_t *var);
|
||||||
|
|
||||||
|
typedef struct consolevar_s {
|
||||||
|
char_t name[CONSOLE_VAR_NAME_MAX];
|
||||||
|
char_t value[CONSOLE_VAR_VALUE_MAX];
|
||||||
|
consolevarchanged_t events[CONSOLE_VAR_EVENTS_MAX];
|
||||||
|
uint8_t eventCount;
|
||||||
|
} consolevar_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a console variable.
|
||||||
|
*
|
||||||
|
* @param var Pointer to the console variable.
|
||||||
|
* @param name The name of the variable.
|
||||||
|
* @param value The initial value of the variable.
|
||||||
|
*/
|
||||||
|
void consoleVarInit(
|
||||||
|
consolevar_t *var,
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a console variable with a listener.
|
||||||
|
*
|
||||||
|
* @param var Pointer to the console variable.
|
||||||
|
* @param name The name of the variable.
|
||||||
|
* @param value The initial value of the variable.
|
||||||
|
* @param event The event to register.
|
||||||
|
*/
|
||||||
|
void consoleVarInitListener(
|
||||||
|
consolevar_t *var,
|
||||||
|
const char_t *name,
|
||||||
|
const char_t *value,
|
||||||
|
consolevarchanged_t event
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of a console variable.
|
||||||
|
*
|
||||||
|
* @param var Pointer to the console variable.
|
||||||
|
* @param value The new value of the variable.
|
||||||
|
*/
|
||||||
|
void consoleVarSetValue(consolevar_t *var, const char_t *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event to be called when the value of a console variable changes.
|
||||||
|
*
|
||||||
|
* @param var Pointer to the console variable.
|
||||||
|
* @param event The event to register.
|
||||||
|
*/
|
||||||
|
void consoleVarListen(consolevar_t *var, consolevarchanged_t event);
|
||||||
+9
-138
@@ -21,76 +21,10 @@
|
|||||||
#include "game/game.h"
|
#include "game/game.h"
|
||||||
#include "physics/physicsmanager.h"
|
#include "physics/physicsmanager.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include "network/networkinfo.h"
|
|
||||||
#include "system/system.h"
|
#include "system/system.h"
|
||||||
|
#include "console/console.h"
|
||||||
#include "display/mesh/cube.h"
|
|
||||||
#include "display/mesh/plane.h"
|
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
entityid_t phBoxEnt;
|
|
||||||
componentid_t phBoxPhys;
|
|
||||||
|
|
||||||
float_t onlineSwapTime = FLT_MAX;
|
|
||||||
|
|
||||||
void goOnline();
|
|
||||||
void goOffline();
|
|
||||||
|
|
||||||
void onNetworkConnected(void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
|
|
||||||
networkinfo_t info = networkGetInfo();
|
|
||||||
if(info.type == NETWORK_TYPE_IPV4) {
|
|
||||||
sceneLog(
|
|
||||||
"Connected to network with IPv4 address: " NETWORK_INFO_FORMAT_IPV4 "\n",
|
|
||||||
info.ipv4.ip[0], info.ipv4.ip[1], info.ipv4.ip[2], info.ipv4.ip[3]
|
|
||||||
);
|
|
||||||
#ifdef DUSK_NETWORK_IPV6
|
|
||||||
} else if(info.type == NETWORK_TYPE_IPV6) {
|
|
||||||
sceneLog(
|
|
||||||
"Connected to network with IPv6 address: " NETWORK_INFO_FORMAT_IPV6 "\n",
|
|
||||||
info.ipv6.ip[0], info.ipv6.ip[1], info.ipv6.ip[2], info.ipv6.ip[3],
|
|
||||||
info.ipv6.ip[4], info.ipv6.ip[5], info.ipv6.ip[6], info.ipv6.ip[7],
|
|
||||||
info.ipv6.ip[8], info.ipv6.ip[9], info.ipv6.ip[10], info.ipv6.ip[11],
|
|
||||||
info.ipv6.ip[12], info.ipv6.ip[13], info.ipv6.ip[14], info.ipv6.ip[15]
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
sceneLog("Network connected, I will disconnect at: %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkFailed(errorret_t error, void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Failed to connect to network, will try again at %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkDisconnected(errorret_t error, void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Network disconnected, will go online at %.2f1.\n", onlineSwapTime);
|
|
||||||
errorCatch(errorPrint(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkDisconnectFinished(void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Finished disconnecting from network, will go online at %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void goOnline() {
|
|
||||||
sceneLog("Going online...\n");
|
|
||||||
networkRequestConnection(
|
|
||||||
onNetworkConnected,
|
|
||||||
onNetworkFailed,
|
|
||||||
onNetworkDisconnected,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void goOffline() {
|
|
||||||
sceneLog("Going offline...\n");
|
|
||||||
networkRequestDisconnection(onNetworkDisconnectFinished, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
@@ -101,6 +35,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
// Init systems. Order is important.
|
// Init systems. Order is important.
|
||||||
errorChain(systemInit());
|
errorChain(systemInit());
|
||||||
timeInit();
|
timeInit();
|
||||||
|
consoleInit();
|
||||||
errorChain(inputInit());
|
errorChain(inputInit());
|
||||||
errorChain(assetInit());
|
errorChain(assetInit());
|
||||||
errorChain(localeManagerInit());
|
errorChain(localeManagerInit());
|
||||||
@@ -110,55 +45,9 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
entityManagerInit();
|
entityManagerInit();
|
||||||
physicsManagerInit();
|
physicsManagerInit();
|
||||||
// errorChain(networkInit());
|
errorChain(networkInit());
|
||||||
errorChain(gameInit());
|
errorChain(gameInit());
|
||||||
|
consolePrint("Engine initialized\n");
|
||||||
sceneLog("Init done, going to queue online in 3 seconds...\n");
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
|
|
||||||
// Camera
|
|
||||||
entityid_t cam = entityManagerAdd();
|
|
||||||
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
|
|
||||||
float_t distance = 6.0f;
|
|
||||||
entityPositionLookAt(
|
|
||||||
cam, camPos,
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
(vec3){ distance, distance, distance }
|
|
||||||
);
|
|
||||||
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
|
|
||||||
entityCameraSetZFar(cam, camCam, 100.0f);
|
|
||||||
|
|
||||||
// Floor
|
|
||||||
entityid_t floorEnt = entityManagerAdd();
|
|
||||||
componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
componentid_t floorMesh = entityAddComponent(floorEnt, COMPONENT_TYPE_MESH);
|
|
||||||
componentid_t floorMat = entityAddComponent(floorEnt, COMPONENT_TYPE_MATERIAL);
|
|
||||||
componentid_t floorPhys = entityAddComponent(floorEnt, COMPONENT_TYPE_PHYSICS);
|
|
||||||
|
|
||||||
entityPositionSetPosition(floorEnt, floorPos, (vec3){ -5.0f, 0.0f, -5.0f });
|
|
||||||
entityPositionSetScale(floorEnt, floorPos, (vec3){ 10.0f, 1.0f, 10.0f });
|
|
||||||
entityMeshSetMesh(floorEnt, floorMesh, &PLANE_MESH_SIMPLE);
|
|
||||||
entityMaterialGetShaderMaterial(floorEnt, floorMat)->unlit.color = COLOR_GREEN;
|
|
||||||
|
|
||||||
entityphysics_t *floorPhysData = entityPhysicsGet(floorEnt, floorPhys);
|
|
||||||
floorPhysData->type = PHYSICS_BODY_STATIC;
|
|
||||||
floorPhysData->shape.type = PHYSICS_SHAPE_PLANE;
|
|
||||||
floorPhysData->shape.data.plane.normal[0] = 0.0f;
|
|
||||||
floorPhysData->shape.data.plane.normal[1] = 1.0f;
|
|
||||||
floorPhysData->shape.data.plane.normal[2] = 0.0f;
|
|
||||||
floorPhysData->shape.data.plane.distance = 0.0f;
|
|
||||||
|
|
||||||
// Box
|
|
||||||
phBoxEnt = entityManagerAdd();
|
|
||||||
componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
|
|
||||||
componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
|
|
||||||
phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS);
|
|
||||||
|
|
||||||
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
|
|
||||||
entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
|
|
||||||
entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f });
|
|
||||||
|
|
||||||
/* Run the init script. */
|
/* Run the init script. */
|
||||||
scriptcontext_t ctx;
|
scriptcontext_t ctx;
|
||||||
@@ -170,38 +59,18 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineUpdate(void) {
|
errorret_t engineUpdate(void) {
|
||||||
// errorChain(networkUpdate());
|
errorChain(networkUpdate());
|
||||||
|
|
||||||
timeUpdate();
|
timeUpdate();
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
|
consoleUpdate();
|
||||||
uiUpdate();
|
uiUpdate();
|
||||||
errorChain(sceneUpdate());
|
errorChain(sceneUpdate());
|
||||||
|
|
||||||
/* Reset the box to its start position on demand. */
|
|
||||||
if(inputIsDown(INPUT_ACTION_ACCEPT)) {
|
|
||||||
componentid_t posComp = entityGetComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
entityPositionSetPosition(phBoxEnt, posComp, (vec3){ 0.0f, 4.0f, 0.0f });
|
|
||||||
entityPhysicsSetVelocity(phBoxEnt, phBoxPhys, (vec3){ 0.0f, 0.0f, 0.0f });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step physics: positions are updated directly on POSITION components. */
|
|
||||||
physicsManagerUpdate();
|
physicsManagerUpdate();
|
||||||
|
|
||||||
errorChain(gameUpdate());
|
errorChain(gameUpdate());
|
||||||
errorChain(displayUpdate());
|
errorChain(displayUpdate());
|
||||||
|
|
||||||
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
||||||
|
|
||||||
if(TIME.time >= onlineSwapTime) {
|
|
||||||
onlineSwapTime = FLT_MAX;
|
|
||||||
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
|
|
||||||
goOffline();
|
|
||||||
} else {
|
|
||||||
goOnline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +79,15 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
// errorChain(networkDispose());
|
|
||||||
sceneDispose();
|
sceneDispose();
|
||||||
errorChain(gameDispose());
|
errorChain(gameDispose());
|
||||||
|
errorChain(networkDispose());
|
||||||
entityManagerDispose();
|
entityManagerDispose();
|
||||||
localeManagerDispose();
|
localeManagerDispose();
|
||||||
uiDispose();
|
uiDispose();
|
||||||
|
consoleDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
errorChain(assetDispose());
|
errorChain(assetDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "entitymanager.h"
|
#include "entitymanager.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "scene/scene.h"
|
#include "console/console.h"
|
||||||
|
|
||||||
entitymanager_t ENTITY_MANAGER;
|
entitymanager_t ENTITY_MANAGER;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ void entityManagerInit(void) {
|
|||||||
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
||||||
);
|
);
|
||||||
|
|
||||||
sceneLog(
|
consolePrint(
|
||||||
"Entity Manager size: %zu bytes (%.2f KB)\n",
|
"Entity Manager size: %zu bytes (%.2f KB)\n",
|
||||||
sizeof(entitymanager_t),
|
sizeof(entitymanager_t),
|
||||||
sizeof(entitymanager_t) / 1024.0f
|
sizeof(entitymanager_t) / 1024.0f
|
||||||
|
|||||||
+3
-56
@@ -15,56 +15,12 @@
|
|||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
scene_t SCENE;
|
scene_t SCENE;
|
||||||
|
|
||||||
char_t SCENE_LOG[SCENE_LOG_SIZE];
|
|
||||||
|
|
||||||
void sceneLog(const char *fmt, ...) {
|
|
||||||
char temp[512];
|
|
||||||
|
|
||||||
// 1. Format input like printf
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(temp, sizeof(temp), fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
printf("%s", temp);
|
|
||||||
|
|
||||||
// 2. Split into lines
|
|
||||||
char *lines[64];
|
|
||||||
int line_count = 0;
|
|
||||||
|
|
||||||
char *ptr = temp;
|
|
||||||
while (*ptr && line_count < 64) {
|
|
||||||
lines[line_count++] = ptr;
|
|
||||||
|
|
||||||
char *nl = strchr(ptr, '\n');
|
|
||||||
if (!nl) break;
|
|
||||||
|
|
||||||
*nl = '\0';
|
|
||||||
ptr = nl + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Prepend lines in reverse order (so final order is correct)
|
|
||||||
for (int i = 0; i < line_count; i++) {
|
|
||||||
char new_log[SCENE_LOG_SIZE];
|
|
||||||
|
|
||||||
snprintf(new_log, sizeof(new_log), "%s\n%s", lines[i], SCENE_LOG);
|
|
||||||
|
|
||||||
// Copy back safely
|
|
||||||
strncpy(SCENE_LOG, new_log, SCENE_LOG_SIZE - 1);
|
|
||||||
SCENE_LOG[SCENE_LOG_SIZE - 1] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t sceneInit(void) {
|
errorret_t sceneInit(void) {
|
||||||
memoryZero(&SCENE, sizeof(scene_t));
|
memoryZero(&SCENE, sizeof(scene_t));
|
||||||
|
|
||||||
memoryZero(SCENE_LOG, sizeof(SCENE_LOG));
|
|
||||||
sceneLog("Init\n");
|
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,17 +133,8 @@ errorret_t sceneRender(void) {
|
|||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
||||||
// errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_0TEXTURE, &DEFAULT_FONT_TEXTURE));
|
|
||||||
// errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
errorChain(consoleDraw());
|
||||||
errorChain(textDraw(
|
|
||||||
32, 32,
|
|
||||||
// "Hello World",
|
|
||||||
SCENE_LOG,
|
|
||||||
COLOR_WHITE,
|
|
||||||
&DEFAULT_FONT_TILESET,
|
|
||||||
&DEFAULT_FONT_TEXTURE
|
|
||||||
));
|
|
||||||
errorChain(spriteBatchFlush());
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ typedef struct {
|
|||||||
|
|
||||||
extern scene_t SCENE;
|
extern scene_t SCENE;
|
||||||
|
|
||||||
#define SCENE_LOG_SIZE 1024
|
|
||||||
extern char_t SCENE_LOG[SCENE_LOG_SIZE];
|
|
||||||
void sceneLog(const char *fmt, ...);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the scene subsystem.
|
* Initialize the scene subsystem.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user