mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Begin working on Emscripten port
- SDL_Mixer has some problems, so sound is disabled - stderr replaced with stdout so the Emscripten page shows some output
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -105,3 +105,4 @@ Thumbs.db
|
||||
src/cdogs/include/
|
||||
src/cdogs/share/
|
||||
*.blend1
|
||||
emscripten/
|
||||
|
32
make_emscripten.sh
Executable file
32
make_emscripten.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf emscripten/*
|
||||
mkdir -p emscripten
|
||||
|
||||
cmake .
|
||||
|
||||
emcc -D "PB_FIELD_16BIT=1" \
|
||||
-Isrc/ \
|
||||
-Isrc/cdogs/ \
|
||||
-Isrc/cdogs/proto/nanopb/ \
|
||||
-Isrc/cdogs/enet/include/ \
|
||||
-Isrc/cdogs/include/ \
|
||||
-Isrc/tests/ \
|
||||
src/*.c \
|
||||
$(find src/cdogs/ -name "*.c") \
|
||||
src/json/*.c \
|
||||
-O3 \
|
||||
-s ASSERTIONS=1 \
|
||||
-s ALLOW_MEMORY_GROWTH=1 \
|
||||
-s USE_SDL=2 \
|
||||
-s USE_SDL_IMAGE=2 \
|
||||
-s SDL2_IMAGE_FORMATS='["png"]' \
|
||||
--preload-file data \
|
||||
--preload-file doc \
|
||||
--preload-file dogfights \
|
||||
--preload-file graphics \
|
||||
--preload-file missions \
|
||||
--preload-file music \
|
||||
--preload-file sounds \
|
||||
-o emscripten/index.html
|
||||
|
@@ -485,6 +485,7 @@ struct option longopts[] =
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -512,5 +513,6 @@ main(int argc, char **argv)
|
||||
printf("flag = '%c'\n", heckle);
|
||||
return 0;
|
||||
}
|
||||
#endif //__EMSCRIPTEN__
|
||||
|
||||
#endif
|
||||
|
@@ -128,6 +128,7 @@ int main(int argc, char *argv[])
|
||||
AutosaveInit(&gAutosave);
|
||||
AutosaveLoad(&gAutosave, GetConfigFilePath(AUTOSAVE_FILE));
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (enet_initialize() != 0)
|
||||
{
|
||||
LOG(LM_MAIN, LL_ERROR, "An error occurred while initializing ENet.");
|
||||
@@ -135,6 +136,7 @@ int main(int argc, char *argv[])
|
||||
goto bail;
|
||||
}
|
||||
NetClientInit(&gNetClient);
|
||||
#endif
|
||||
|
||||
// Print command line
|
||||
char buf[CDOGS_PATH_MAX];
|
||||
@@ -146,9 +148,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
debug(D_NORMAL, "Initialising SDL...\n");
|
||||
#ifndef __EMSCRIPTEN__
|
||||
const int sdlFlags =
|
||||
SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_HAPTIC |
|
||||
SDL_INIT_GAMECONTROLLER;
|
||||
#else
|
||||
const int sdlFlags = SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
|
||||
#endif
|
||||
if (SDL_Init(sdlFlags) != 0)
|
||||
{
|
||||
LOG(LM_MAIN, LL_ERROR, "Could not initialise SDL: %s", SDL_GetError());
|
||||
@@ -168,6 +174,7 @@ int main(int argc, char *argv[])
|
||||
LOG(LM_MAIN, LL_INFO, "data dir(%s)", buf);
|
||||
LOG(LM_MAIN, LL_INFO, "config dir(%s)", GetConfigFilePath(""));
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SoundInitialize(&gSoundDevice, "sounds");
|
||||
if (!gSoundDevice.isInitialised)
|
||||
{
|
||||
@@ -177,6 +184,7 @@ int main(int argc, char *argv[])
|
||||
LoadSongs();
|
||||
|
||||
MusicPlayMenu(&gSoundDevice);
|
||||
#endif
|
||||
|
||||
EventInit(&gEventHandlers, NULL, NULL, true);
|
||||
NetServerInit(&gNetServer);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* A unit test and example of how to use the simple C hashmap
|
||||
*/
|
||||
#ifndef __EMSCRIPTEN__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -78,4 +79,5 @@ int main(char* argv, int argc)
|
||||
hashmap_free(mymap);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif //__EMSCRIPTEN__
|
||||
|
@@ -54,6 +54,10 @@
|
||||
#include <sys/poll.h>
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define HAS_SOCKLEN_T 1
|
||||
#endif
|
||||
|
||||
#ifndef HAS_SOCKLEN_T
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
@@ -55,7 +55,11 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
#include <tinydir/tinydir.h>
|
||||
|
||||
#include "actors.h"
|
||||
|
@@ -31,6 +31,10 @@
|
||||
|
||||
#include "sys_specifics.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define stderr stdout
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
@@ -31,7 +31,11 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "gamedata.h"
|
||||
|
@@ -82,6 +82,7 @@ int OpenAudio(int frequency, Uint16 format, int channels, int chunkSize)
|
||||
}
|
||||
|
||||
// Check that we got the specs we wanted
|
||||
#ifndef __EMSCRIPTEN__
|
||||
Mix_QuerySpec(&qFrequency, &qFormat, &qChannels);
|
||||
debug(D_NORMAL, "spec: f=%d fmt=%d c=%d\n", qFrequency, qFormat, qChannels);
|
||||
if (qFrequency != frequency || qFormat != format || qChannels != channels)
|
||||
@@ -89,6 +90,7 @@ int OpenAudio(int frequency, Uint16 format, int channels, int chunkSize)
|
||||
printf("Audio not what we want.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -370,6 +372,7 @@ static void SoundPlayAtPosition(
|
||||
// When allocating new channels, need to reset their volume
|
||||
Mix_Volume(-1, ConfigGetInt(&gConfig, "Sound.SoundVolume"));
|
||||
}
|
||||
#ifndef __EMSCRIPTEN__
|
||||
Mix_SetPosition(channel, (Sint16)bearing, (Uint8)distance);
|
||||
if (isMuffled)
|
||||
{
|
||||
@@ -378,6 +381,7 @@ static void SoundPlayAtPosition(
|
||||
fprintf(stderr, "Mix_RegisterEffect: %s\n", Mix_GetError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SoundPlay(SoundDevice *device, Mix_Chunk *data)
|
||||
|
@@ -50,7 +50,12 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL.h>
|
||||
#include <SDL/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
|
||||
#include "c_array.h"
|
||||
#include "c_hashmap/hashmap.h"
|
||||
|
@@ -48,7 +48,12 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL.h>
|
||||
#include <SDL/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
|
||||
#include "c_array.h"
|
||||
#include "game_events.h"
|
||||
|
@@ -46,6 +46,8 @@
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __EMSCRIPTEN__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -1421,3 +1423,4 @@ int main(int argc, char *argv[])
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
#endif //__EMSCRIPTEN__
|
||||
|
68
src/emscripten_loop.h
Normal file
68
src/emscripten_loop.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (C) 1995 Ronny Wester
|
||||
Copyright (C) 2003 Jeremy Chin
|
||||
Copyright (C) 2003-2007 Lucas Martin-King
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright (c) 2013-2017, Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <stdbool.h>
|
||||
#include "credits.h"
|
||||
#include "cdogs/campaigns.h"
|
||||
#include "cdogs/game_mode.h"
|
||||
|
||||
struct emscripten_context_t {
|
||||
GameMode lastGameMode;
|
||||
bool wasClient;
|
||||
credits_displayer_t *creditsDisplayer;
|
||||
custom_campaigns_t *campaigns;
|
||||
};
|
||||
|
||||
struct emscripten_context_t EmscriptenContext;
|
||||
|
||||
void EmscriptenMainLoop(void *arg);
|
||||
|
||||
#endif
|
175
src/game_loop.c
175
src/game_loop.c
@@ -36,6 +36,9 @@
|
||||
#include "net_server.h"
|
||||
#include "sounds.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
GameLoopData *GameLoopDataNew(
|
||||
void *data,
|
||||
@@ -93,9 +96,96 @@ typedef struct
|
||||
int FramesSkipped;
|
||||
int MaxFrameskip;
|
||||
} LoopRunParams;
|
||||
typedef struct
|
||||
{
|
||||
LoopRunner *l;
|
||||
GameLoopData *data;
|
||||
LoopRunParams p;
|
||||
}LoopRunInnerData;
|
||||
static LoopRunParams LoopRunParamsNew(const GameLoopData *data);
|
||||
static bool LoopRunParamsShouldSleep(LoopRunParams *p);
|
||||
static bool LoopRunParamsShouldSkip(LoopRunParams *p);
|
||||
bool LoopRunnerRunInner(LoopRunInnerData *ctx)
|
||||
{
|
||||
// Frame rate control
|
||||
if (LoopRunParamsShouldSleep(&(ctx->p)))
|
||||
{
|
||||
SDL_Delay(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Input
|
||||
if ((ctx->data->Frames & 1) || !ctx->data->InputEverySecondFrame)
|
||||
{
|
||||
EventPoll(&gEventHandlers, ctx->p.TicksNow);
|
||||
if (ctx->data->InputFunc)
|
||||
{
|
||||
ctx->data->InputFunc(ctx->data);
|
||||
}
|
||||
}
|
||||
|
||||
NetClientPoll(&gNetClient);
|
||||
NetServerPoll(&gNetServer);
|
||||
|
||||
// Update
|
||||
ctx->p.Result = ctx->data->UpdateFunc(ctx->data, ctx->l);
|
||||
GameLoopData *newData = GetCurrentLoop(ctx->l);
|
||||
if (newData == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (newData != ctx->data)
|
||||
{
|
||||
// State change; restart loop
|
||||
GameLoopOnExit(ctx->data);
|
||||
ctx->data = newData;
|
||||
GameLoopOnEnter(ctx->data);
|
||||
ctx->p = LoopRunParamsNew(ctx->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
NetServerFlush(&gNetServer);
|
||||
NetClientFlush(&gNetClient);
|
||||
|
||||
bool draw = !ctx->data->HasDrawnFirst;
|
||||
switch (ctx->p.Result)
|
||||
{
|
||||
case UPDATE_RESULT_OK:
|
||||
// Do nothing
|
||||
break;
|
||||
case UPDATE_RESULT_DRAW:
|
||||
draw = true;
|
||||
break;
|
||||
default:
|
||||
CASSERT(false, "Unknown loop result");
|
||||
break;
|
||||
}
|
||||
ctx->data->Frames++;
|
||||
// frame skip
|
||||
if (LoopRunParamsShouldSkip(&(ctx->p)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (draw)
|
||||
{
|
||||
if (ctx->data->DrawFunc)
|
||||
{
|
||||
ctx->data->DrawFunc(ctx->data);
|
||||
BlitFlip(&gGraphicsDevice);
|
||||
}
|
||||
ctx->data->HasDrawnFirst = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
void EmscriptenMainLoop(void *arg)
|
||||
{
|
||||
// struct emscripten_context_t *ctx = arg;
|
||||
LoopRunnerRunInner((LoopRunInnerData *)arg);
|
||||
}
|
||||
#endif
|
||||
void LoopRunnerRun(LoopRunner *l)
|
||||
{
|
||||
GameLoopData *data = GetCurrentLoop(l);
|
||||
@@ -105,80 +195,23 @@ void LoopRunnerRun(LoopRunner *l)
|
||||
}
|
||||
GameLoopOnEnter(data);
|
||||
|
||||
LoopRunParams p = LoopRunParamsNew(data);
|
||||
LoopRunInnerData ctx;
|
||||
ctx.l = l;
|
||||
ctx.data = data;
|
||||
ctx.p = LoopRunParamsNew(data);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// TODO use GameLoopData->FPS instead of 30?
|
||||
emscripten_set_main_loop_arg(EmscriptenMainLoop, &ctx, 0, 1);
|
||||
#else
|
||||
for (;;)
|
||||
{
|
||||
// Frame rate control
|
||||
if (LoopRunParamsShouldSleep(&p))
|
||||
{
|
||||
SDL_Delay(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Input
|
||||
if ((data->Frames & 1) || !data->InputEverySecondFrame)
|
||||
{
|
||||
EventPoll(&gEventHandlers, p.TicksNow);
|
||||
if (data->InputFunc)
|
||||
{
|
||||
data->InputFunc(data);
|
||||
}
|
||||
}
|
||||
|
||||
NetClientPoll(&gNetClient);
|
||||
NetServerPoll(&gNetServer);
|
||||
|
||||
// Update
|
||||
p.Result = data->UpdateFunc(data, l);
|
||||
GameLoopData *newData = GetCurrentLoop(l);
|
||||
if (newData == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (newData != data)
|
||||
{
|
||||
// State change; restart loop
|
||||
GameLoopOnExit(data);
|
||||
data = newData;
|
||||
GameLoopOnEnter(data);
|
||||
p = LoopRunParamsNew(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
NetServerFlush(&gNetServer);
|
||||
NetClientFlush(&gNetClient);
|
||||
|
||||
bool draw = !data->HasDrawnFirst;
|
||||
switch (p.Result)
|
||||
{
|
||||
case UPDATE_RESULT_OK:
|
||||
// Do nothing
|
||||
break;
|
||||
case UPDATE_RESULT_DRAW:
|
||||
draw = true;
|
||||
break;
|
||||
default:
|
||||
CASSERT(false, "Unknown loop result");
|
||||
break;
|
||||
}
|
||||
data->Frames++;
|
||||
// frame skip
|
||||
if (LoopRunParamsShouldSkip(&p))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (draw)
|
||||
{
|
||||
if (data->DrawFunc)
|
||||
{
|
||||
data->DrawFunc(data);
|
||||
BlitFlip(&gGraphicsDevice);
|
||||
}
|
||||
data->HasDrawnFirst = true;
|
||||
}
|
||||
if (!LoopRunnerRunInner(&ctx))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
GameLoopOnExit(data);
|
||||
}
|
||||
static LoopRunParams LoopRunParamsNew(const GameLoopData *data)
|
||||
|
Reference in New Issue
Block a user