yeah whatever

This commit is contained in:
2026-07-18 23:07:45 -05:00
parent 85b6109792
commit ef78f023ed
8 changed files with 169 additions and 273 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
# Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
# bba
bba
)
# ISO post-build: produce NTSC-J, NTSC-U and PAL disc images
+1 -153
View File
@@ -12,20 +12,15 @@
#include "locale/localemanager.h"
#include "display/display.h"
#include "scene/scene.h"
#include "entity/entitymanager.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entityrenderable.h"
#include "entity/component/physics/entityphysics.h"
#include "display/color.h"
#include "asset/asset.h"
#include "ui/ui.h"
#include "assert/assert.h"
#include "network/network.h"
#include "network/http/networkhttprequest.h"
#include "system/system.h"
#include "console/console.h"
#include "save/save.h"
#include "save/savesettings.h"
#include "log/log.h"
engine_t ENGINE;
@@ -51,85 +46,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(networkInit());
errorChain(sceneInit());
// Test: three colored cubes falling onto each other and a static floor,
// viewed by a static camera.
ENGINE.testSceneId = sceneCreate();
sceneSetActive(ENGINE.testSceneId);
entitymanager_t *testEntities = sceneGetEntities(ENGINE.testSceneId);
entityid_t testCamera = entityManagerAdd(testEntities);
componentid_t testCameraPosition = entityAddComponent(
testEntities, testCamera, COMPONENT_TYPE_POSITION
);
entityAddComponent(testEntities, testCamera, COMPONENT_TYPE_CAMERA);
entityPositionLookAt(
testEntities, testCamera, testCameraPosition,
(vec3){ 4.0f, 3.0f, 4.0f },
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f }
);
// Static floor: a wide, thin box. Its render scale matches its physics
// half-extents exactly, since CUBE_MESH_SIMPLE and PHYSICS_SHAPE_CUBE are
// both centered on the entity's position.
entityid_t floorEntity = entityManagerAdd(testEntities);
componentid_t floorPosition = entityAddComponent(
testEntities, floorEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
testEntities, floorEntity, floorPosition, (vec3){ 0.0f, -1.0f, 0.0f }
);
entityPositionSetLocalScale(
testEntities, floorEntity, floorPosition, (vec3){ 10.0f, 1.0f, 10.0f }
);
componentid_t floorPhysics = entityAddComponent(
testEntities, floorEntity, COMPONENT_TYPE_PHYSICS
);
entityPhysicsSetBodyType(
testEntities, floorEntity, floorPhysics, PHYSICS_BODY_STATIC
);
entityPhysicsSetShape(testEntities, floorEntity, floorPhysics, (physicsshape_t){
.type = PHYSICS_SHAPE_CUBE,
.data.cube.halfExtents = { 5.0f, 0.5f, 5.0f }
});
componentid_t floorRenderable = entityAddComponent(
testEntities, floorEntity, COMPONENT_TYPE_RENDERABLE
);
entityRenderableSetColor(testEntities, floorEntity, floorRenderable, COLOR_GRAY);
// Three dynamic cubes, staggered above the floor with slight offsets so
// they tumble and land on each other instead of falling in perfect sync.
vec3 cubeStartPositions[3] = {
{ -0.3f, 1.5f, 0.1f },
{ 0.1f, 3.0f, -0.2f },
{ -0.1f, 4.5f, 0.2f },
};
color_t cubeColors[3] = { COLOR_RED, COLOR_GREEN, COLOR_BLUE };
for(uint8_t i = 0; i < 3; i++) {
entityid_t cubeEntity = entityManagerAdd(testEntities);
componentid_t cubePosition = entityAddComponent(
testEntities, cubeEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
testEntities, cubeEntity, cubePosition, cubeStartPositions[i]
);
entityAddComponent(testEntities, cubeEntity, COMPONENT_TYPE_PHYSICS);
componentid_t cubeRenderable = entityAddComponent(
testEntities, cubeEntity, COMPONENT_TYPE_RENDERABLE
);
entityRenderableSetColor(
testEntities, cubeEntity, cubeRenderable, cubeColors[i]
);
}
networkRequestConnection(
engineNetworkOnConnected,
engineNetworkOnFailed,
engineNetworkOnDisconnect,
NULL
);
consolePrint("Engine initialized");
#ifdef DUSK_ASSERTIONS_FAKED
@@ -148,14 +64,6 @@ errorret_t engineUpdate(void) {
errorChain(networkUpdate());
timeUpdate();
if(
ENGINE.networkDisconnectTestPending &&
TIME.time >= ENGINE.networkDisconnectTestAt
) {
ENGINE.networkDisconnectTestPending = false;
networkRequestDisconnection(engineNetworkDisconnectTestOnComplete, NULL);
}
const systemdialogtype_t dialogType = systemGetActiveDialogType();
if(dialogType == SYSTEM_DIALOG_TYPE_NONE) {
inputUpdate();
@@ -193,63 +101,3 @@ errorret_t engineDispose(void) {
errorOk();
}
void engineHttpTestOnComplete(void *params, void *user) {
networkhttprequest_t *request = (networkhttprequest_t *)params;
const networkhttpresponse_t *response = &request->response;
consolePrint("frogfind.com response status: %d", response->status);
for(uint32_t i = 0; i < response->headers.count; i++) {
const networkhttpheader_t *header = &response->headers.headers[i];
consolePrint("frogfind.com header: %s: %s", header->name, header->value);
}
consolePrint("frogfind.com body length: %d", (int32_t)response->bodyLength);
const size_t chunkMax = CONSOLE_LINE_MAX - 32;
for(size_t offset = 0; offset < response->bodyLength; offset += chunkMax) {
size_t chunkLength = response->bodyLength - offset;
if(chunkLength > chunkMax) chunkLength = chunkMax;
consolePrint("frogfind.com body: %.*s",
(int32_t)chunkLength, (const char_t *)response->body + offset);
}
}
void engineHttpTestOnError(void *params, void *user) {
consolePrint("frogfind.com request failed");
}
void engineNetworkOnConnected(void *user) {
consolePrint("Network connected");
errorret_t ret = networkHttpRequest(
NETWORK_HTTP_METHOD_GET,
"http://frogfind.com",
NULL, 0,
NULL, 0,
NULL, 0,
engineHttpTestOnComplete,
engineHttpTestOnError,
NULL
);
errorCatch(errorPrint(ret));
ENGINE.networkDisconnectTestAt = TIME.time + 10.0f;
ENGINE.networkDisconnectTestPending = true;
}
void engineNetworkOnFailed(errorret_t error, void *user) {
consolePrint("Network connection failed");
errorCatch(errorPrint(error));
}
void engineNetworkOnDisconnect(errorret_t error, void *user) {
consolePrint("Network disconnected");
errorCatch(errorPrint(error));
}
void engineNetworkDisconnectTestOnComplete(void *user) {
consolePrint("Network disconnect test complete");
}
-59
View File
@@ -10,22 +10,12 @@
// Important to be included first:
#include "display/display.h"
#include "error/error.h"
#include "scene/scenebase.h"
#include "entity/entitybase.h"
typedef struct {
bool_t running;
int32_t argc;
const char_t **argv;
const char_t *version;
// Test: disconnects the network 10 seconds after it connects.
bool_t networkDisconnectTestPending;
float_t networkDisconnectTestAt;
// Test: three colored cubes falling onto each other and a static floor,
// viewed by a static camera.
sceneid_t testSceneId;
} engine_t;
extern engine_t ENGINE;
@@ -47,52 +37,3 @@ errorret_t engineUpdate(void);
* Shuts down the engine.
*/
errorret_t engineDispose(void);
/**
* Logs the response status, headers and body of the frogfind.com test
* request to the console.
*
* @param params The completed networkhttprequest_t.
* @param user Unused.
*/
void engineHttpTestOnComplete(void *params, void *user);
/**
* Logs that the frogfind.com test request failed.
*
* @param params The failed networkhttprequest_t.
* @param user Unused.
*/
void engineHttpTestOnError(void *params, void *user);
/**
* Fires the frogfind.com test request once the network connection is
* up (some platforms, such as PSP, only bring the network stack up
* asynchronously after networkRequestConnection is called).
*
* @param user Unused.
*/
void engineNetworkOnConnected(void *user);
/**
* Logs that the network connection could not be established.
*
* @param error The error describing why the connection failed.
* @param user Unused.
*/
void engineNetworkOnFailed(errorret_t error, void *user);
/**
* Logs that the network connection was lost after having connected.
*
* @param error The error describing why the connection was lost.
* @param user Unused.
*/
void engineNetworkOnDisconnect(errorret_t error, void *user);
/**
* Logs that the 10-second test disconnect completed.
*
* @param user Unused.
*/
void engineNetworkDisconnectTestOnComplete(void *user);
+8 -6
View File
@@ -116,16 +116,18 @@ void logError(const char_t *message, ...) {
// PAD_Init is idempotent - safe even if inputInit already called it,
// and handles the case where the error occurred before inputInit ran.
// logError is used for routine, already-handled errors (e.g. a rejected
// network connection) as well as genuine assertion failures, so it must
// only pause to let the message be seen, not decide to terminate the
// program -- that decision belongs to the caller (assertTrueImpl calls
// abort() right after logError returns; a plain errorPrint() just
// continues running).
PAD_Init();
while(SYS_MainLoop()) {
PAD_ScanPads();
// START button matches the RAGEQUIT bind - allows exit on GC controller
// when there is no Wiimote HOME button available.
// START button matches the RAGEQUIT bind - allows dismissing the
// message on a GC controller when there is no Wiimote HOME button.
if(PAD_ButtonsDown(0) & PAD_BUTTON_START) break;
VIDEO_WaitVSync();
}
// Exit cleanly to HBC regardless of engine state. engineDispose() is not
// called here because the engine may be partially initialized.
exit(0);
}
+78 -32
View File
@@ -7,7 +7,10 @@
#include "network/network.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#include "log/log.h"
#include <ogc/system.h>
errorret_t networkDolphinInit() {
// s32 ret = net_init();
@@ -45,43 +48,86 @@ void networkDolphinRequestConnection(
NETWORK.platform.onFailed = onFailed;
NETWORK.platform.onConnectedUser = user;
memoryZero(NETWORK.platform.ip, sizeof(NETWORK.platform.ip));
memoryZero(NETWORK.platform.netmask, sizeof(NETWORK.platform.netmask));
memoryZero(NETWORK.platform.gateway, sizeof(NETWORK.platform.gateway));
// TEMPORARY: static IP instead of DHCP, to isolate whether if_config()'s
// DHCP handshake (and the packet-receive/DMA path it triggers in gcif.c)
// is what's corrupting memory, vs. link-up/NWAY negotiation alone. These
// match Dolphin's Broadband Adapter (Built-In) default subnet. Revert to
// DHCP once this is diagnosed.
stringCopy(NETWORK.platform.ip, "10.0.1.10", sizeof(NETWORK.platform.ip));
stringCopy(
NETWORK.platform.netmask, "255.255.255.0",
sizeof(NETWORK.platform.netmask)
);
stringCopy(
NETWORK.platform.gateway, "10.0.1.1", sizeof(NETWORK.platform.gateway)
);
// Negotiate DHCP using the Wi-Fi settings saved in Wii System Menu.
// This call blocks until the interface is configured or times out.
#ifdef DUSK_WII
s32 ret = if_config(
NETWORK.platform.ip,
NETWORK.platform.netmask,
NETWORK.platform.gateway,
true
);
#else
s32 ret = -1;
#endif
// Diagnostic: log the live MEM1 arena headroom (assets/heap already
// accounted for, since this runs after assetInit()/scene setup) right
// around if_config(), to check whether linking `bba` (~400KB+ of new
// static lwIP pools, see lwipopts.h) is pushing memory too tight.
logDebug(
"networkDolphinRequestConnection: MEM1 arena free before if_config(): "
"%u bytes (lo=%p hi=%p)\n",
(uint32_t)((uint8_t *)SYS_GetArena1Hi() - (uint8_t *)SYS_GetArena1Lo()),
SYS_GetArena1Lo(), SYS_GetArena1Hi()
);
// Negotiate DHCP. This call blocks until the interface is configured or
// times out. Wii (IOS) and GameCube (bundled lwIP + BBA driver, linked
// via `bba` in gamecube.cmake) expose the same if_config()/net_*() API,
// so this call is identical on both platforms.
logDebug("networkDolphinRequestConnection: calling if_config()\n");
s32 ret = if_config(
NETWORK.platform.ip,
NETWORK.platform.netmask,
NETWORK.platform.gateway,
false
);
logDebug(
"networkDolphinRequestConnection: if_config() returned %d "
"(ip=%s netmask=%s gateway=%s)\n",
(int_t)ret,
NETWORK.platform.ip,
NETWORK.platform.netmask,
NETWORK.platform.gateway
);
logDebug(
"networkDolphinRequestConnection: MEM1 arena free after if_config(): "
"%u bytes (lo=%p hi=%p)\n",
(uint32_t)((uint8_t *)SYS_GetArena1Hi() - (uint8_t *)SYS_GetArena1Lo()),
SYS_GetArena1Lo(), SYS_GetArena1Hi()
);
// Rejecting the connection (no cable/AP, DHCP timeout) is a routine,
// expected outcome, not a programming error. It must never be able to
// bring the whole program down, so this only logs and skips the
// callback if it's unexpectedly missing, rather than asserting.
if(ret >= 0) {
NETWORK.state = NETWORK_STATE_CONNECTED;
assertNotNull(
NETWORK.platform.onConnected,
"Network platform onConnected callback should be set."
);
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
} else {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
assertNotNull(
NETWORK.platform.onFailed,
"Network platform onFailed callback should be set."
);
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
logDebug("networkDolphinRequestConnection: connected\n");
if(NETWORK.platform.onConnected) {
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
}
return;
}
NETWORK.state = NETWORK_STATE_DISCONNECTED;
logDebug(
"networkDolphinRequestConnection: failed to connect (if_config "
"returned %d)\n",
(int_t)ret
);
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
if(NETWORK.platform.onFailed) {
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
} else {
errorCatch(errorPrint(error));
}
}
+19 -8
View File
@@ -12,6 +12,15 @@
#define NETWORK_DOLPHIN_IP_MAX 16
#ifdef DUSK_GAMECUBE
// No DNS client is implemented for GameCube yet (see
// networkSocketDolphinConnect) -- only literal IP addresses work.
// A future resolver should query this server.
#ifndef NETWORK_DOLPHIN_DNS_SERVER_IP
#define NETWORK_DOLPHIN_DNS_SERVER_IP "0.0.0.0"
#endif
#endif
typedef struct {
char_t ip[NETWORK_DOLPHIN_IP_MAX];
char_t netmask[NETWORK_DOLPHIN_IP_MAX];
@@ -23,37 +32,39 @@ typedef struct {
} networkdolphin_t;
/**
* Initializes the Wii network stack via IOS. Does not connect; use
* networkDolphinRequestConnection for that.
* Initializes the network stack (IOS on Wii, bundled lwIP + BBA driver on
* GameCube). Does not connect; use networkDolphinRequestConnection for
* that.
*
* @return Error state (if any).
*/
errorret_t networkDolphinInit();
/**
* Called each frame. No-op on Wii since connection is synchronous.
* Called each frame. No-op since connection is synchronous.
*
* @return Error state (if any).
*/
errorret_t networkDolphinUpdate();
/**
* Disposes the Wii network stack.
* Disposes the network stack.
*
* @return Error state (if any).
*/
errorret_t networkDolphinDispose();
/**
* Returns true if the Wii is connected to a network.
* Returns true if connected to a network.
*
* @return True if connected.
*/
bool_t networkDolphinIsConnected();
/**
* Requests the Wii to connect to the network using the Wi-Fi settings saved
* in the Wii System Menu. Blocks until connected or failed.
* Requests a connection to the network: the Wi-Fi settings saved in the
* Wii System Menu on Wii, or DHCP over the Broadband Adapter on GameCube.
* Blocks until connected or failed.
*
* @param onConnected Callback on successful connection.
* @param onFailed Callback if connection fails.
@@ -68,7 +79,7 @@ void networkDolphinRequestConnection(
);
/**
* Requests the Wii to disconnect from the network.
* Requests to disconnect from the network.
*
* @param onComplete Callback when disconnection is complete.
* @param user User data passed to the callback.
+58 -12
View File
@@ -8,8 +8,9 @@
#include "networksocketdolphin.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "log/log.h"
#ifdef DUSK_WII
#if defined(DUSK_WII) || defined(DUSK_GAMECUBE)
#include <network.h>
#endif
@@ -21,24 +22,60 @@ errorret_t networkSocketDolphinConnect(
assertNotNull(sock, "sock must not be NULL");
assertNotNull(host, "host must not be NULL");
#ifdef DUSK_WII
struct hostent *entry = net_gethostbyname(host);
if(entry == NULL || entry->h_addr_list[0] == NULL) {
errorThrow("Failed to resolve host %s", host);
}
logDebug(
"networkSocketDolphinConnect: resolving %s:%u\n", host, (uint32_t)port
);
#if defined(DUSK_WII) || defined(DUSK_GAMECUBE)
struct in_addr resolved;
#ifdef DUSK_WII
struct hostent *entry = net_gethostbyname(host);
if(entry == NULL || entry->h_addr_list[0] == NULL) {
logDebug(
"networkSocketDolphinConnect: net_gethostbyname(%s) failed\n", host
);
errorThrow("Failed to resolve host %s", host);
}
memoryCopy(&resolved, entry->h_addr_list[0], sizeof(resolved));
#else
// GameCube's bundled lwIP stack has no DNS resolver (see
// NETWORK_DOLPHIN_DNS_SERVER_IP in networkdolphin.h) -- only
// literal IP addresses work until a resolver is written against
// that server.
if(!inet_aton(host, &resolved)) {
logDebug(
"networkSocketDolphinConnect: inet_aton(%s) failed\n", host
);
errorThrow(
"Failed to resolve host %s (GameCube requires a literal IP "
"address; DNS is not yet supported)", host
);
}
#endif
logDebug(
"networkSocketDolphinConnect: resolved %s to %s\n",
host, inet_ntoa(resolved)
);
const s32 fd = net_socket(AF_INET, SOCK_STREAM, 0);
logDebug(
"networkSocketDolphinConnect: net_socket() returned fd=%d\n",
(int_t)fd
);
if(fd < 0) errorThrow("Failed to create socket: %d", (int_t)fd);
struct sockaddr_in serverAddr;
memoryZero(&serverAddr, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
memoryCopy(
&serverAddr.sin_addr, entry->h_addr_list[0], sizeof(serverAddr.sin_addr)
);
serverAddr.sin_addr = resolved;
const s32 ret = net_connect(fd, (void *)&serverAddr, sizeof(serverAddr));
logDebug(
"networkSocketDolphinConnect: net_connect() returned %d\n", (int_t)ret
);
if(ret < 0) {
net_close(fd);
errorThrow("Failed to connect to %s:%u: %d", host, port, (int_t)ret);
@@ -63,8 +100,12 @@ errorret_t networkSocketDolphinSend(
assertNotNull(data, "data must not be NULL");
assertNotNull(outSent, "outSent must not be NULL");
#ifdef DUSK_WII
#if defined(DUSK_WII) || defined(DUSK_GAMECUBE)
const s32 sent = net_send(sock->fd, data, (s32)length, 0);
logDebug(
"networkSocketDolphinSend: net_send(fd=%d, len=%u) returned %d\n",
sock->fd, (uint32_t)length, (int_t)sent
);
if(sent < 0) errorThrow("Failed to send data: %d", (int_t)sent);
*outSent = (size_t)sent;
@@ -84,8 +125,12 @@ errorret_t networkSocketDolphinReceive(
assertNotNull(buffer, "buffer must not be NULL");
assertNotNull(outReceived, "outReceived must not be NULL");
#ifdef DUSK_WII
#if defined(DUSK_WII) || defined(DUSK_GAMECUBE)
const s32 received = net_recv(sock->fd, buffer, (s32)bufferSize, 0);
logDebug(
"networkSocketDolphinReceive: net_recv(fd=%d, max=%u) returned %d\n",
sock->fd, (uint32_t)bufferSize, (int_t)received
);
if(received < 0) errorThrow("Failed to receive data: %d", (int_t)received);
*outReceived = (size_t)received;
@@ -98,7 +143,8 @@ errorret_t networkSocketDolphinReceive(
void networkSocketDolphinClose(networksocketdolphin_t *sock) {
assertNotNull(sock, "sock must not be NULL");
#ifdef DUSK_WII
#if defined(DUSK_WII) || defined(DUSK_GAMECUBE)
logDebug("networkSocketDolphinClose: closing fd=%d\n", sock->fd);
if(sock->fd >= 0) net_close(sock->fd);
#endif
sock->fd = -1;
@@ -14,8 +14,10 @@ typedef struct {
/**
* Resolves host and opens a blocking TCP connection to host:port.
* Always fails under plain DUSK_GAMECUBE -- retail GameCube units have
* no network hardware.
* On Wii, host may be a hostname (resolved via IOS's DNS resolver) or an
* IP address. On GameCube, host must be a literal IP address -- the
* bundled lwIP stack has no DNS resolver yet (see
* NETWORK_DOLPHIN_DNS_SERVER_IP in networkdolphin.h).
*
* @param sock The socket structure to initialize.
* @param host The hostname or IP address to connect to.