mirror of https://github.com/bjornbytes/lovr.git
commit
6756a94cb9
|
@ -39,3 +39,4 @@ deps/VrApi
|
|||
deps/pico
|
||||
deps/openxr
|
||||
deps/OpenXR-Oculus
|
||||
/plugins/*
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[submodule "deps/enet"]
|
||||
path = deps/enet
|
||||
url = https://github.com/lsalzman/enet
|
||||
[submodule "deps/glfw"]
|
||||
path = deps/glfw
|
||||
url = https://github.com/glfw/glfw
|
||||
|
|
122
CMakeLists.txt
122
CMakeLists.txt
|
@ -14,9 +14,6 @@ option(LOVR_ENABLE_PHYSICS "Enable the physics module" ON)
|
|||
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
|
||||
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)
|
||||
|
||||
option(LOVR_ENABLE_ENET "Bundle with lua-enet" ON)
|
||||
option(LOVR_ENABLE_JSON "Bundle with lua-cjson" ON)
|
||||
|
||||
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
|
||||
option(LOVR_USE_OPENVR "Enable the OpenVR backend for the headset module" ON)
|
||||
option(LOVR_USE_OPENXR "Enable the OpenXR backend for the headset module" OFF)
|
||||
|
@ -27,7 +24,6 @@ option(LOVR_USE_PICO "Enable the Pico backend for the headset module" OFF)
|
|||
option(LOVR_USE_DESKTOP_HEADSET "Enable the keyboard/mouse backend for the headset module" ON)
|
||||
option(LOVR_USE_LINUX_EGL "Use the EGL graphics extension on Linux" OFF)
|
||||
|
||||
option(LOVR_SYSTEM_ENET "Use the system-provided enet" OFF)
|
||||
option(LOVR_SYSTEM_GLFW "Use the system-provided glfw" OFF)
|
||||
option(LOVR_SYSTEM_LUA "Use the system-provided Lua" OFF)
|
||||
option(LOVR_SYSTEM_ODE "Use the system-provided ODE" OFF)
|
||||
|
@ -71,22 +67,6 @@ elseif(UNIX)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# enet
|
||||
if(LOVR_ENABLE_ENET AND NOT EMSCRIPTEN)
|
||||
if(LOVR_SYSTEM_ENET)
|
||||
pkg_search_module(ENET REQUIRED enet)
|
||||
include_directories(${ENET_INCLUDE_DIRS})
|
||||
set(LOVR_ENET ${ENET_LIBRARIES})
|
||||
else()
|
||||
add_subdirectory(deps/enet enet)
|
||||
include_directories(deps/enet/include)
|
||||
set(LOVR_ENET enet)
|
||||
if(WIN32)
|
||||
set(LOVR_ENET ${LOVR_ENET} ws2_32 winmm)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GLFW
|
||||
if(NOT (EMSCRIPTEN OR ANDROID))
|
||||
if(LOVR_SYSTEM_GLFW)
|
||||
|
@ -296,23 +276,20 @@ endif()
|
|||
|
||||
# LÖVR
|
||||
|
||||
# Resources
|
||||
file(GLOB LOVR_RESOURCES "src/resources/*.ttf" "src/resources/*.json" "src/resources/*.lua")
|
||||
foreach(path ${LOVR_RESOURCES})
|
||||
|
||||
# Turn the absolute path into a C variable like src_resources_boot_lua
|
||||
file(RELATIVE_PATH identifier ${CMAKE_CURRENT_SOURCE_DIR} ${path})
|
||||
string(MAKE_C_IDENTIFIER ${identifier} identifier)
|
||||
|
||||
# Read the file and turn the bytes into comma-separated hex literals
|
||||
file(READ ${path} data HEX)
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," data ${data})
|
||||
|
||||
# Generate the output filename by adding .h to the input filename
|
||||
string(CONCAT output ${path} ".h")
|
||||
|
||||
# Write some xxd-compatible C code!
|
||||
file(WRITE ${output} "const unsigned char ${identifier}[] = {${data}};\nconst unsigned int ${identifier}_len = sizeof(${identifier});\n")
|
||||
# Plugins
|
||||
set(LOVR 1)
|
||||
link_libraries(${LOVR_LUA})
|
||||
file(GLOB LOVR_PLUGINS plugins/*)
|
||||
foreach(plugin ${LOVR_PLUGINS})
|
||||
if(IS_DIRECTORY ${plugin} AND EXISTS ${plugin}/CMakeLists.txt)
|
||||
add_subdirectory(${plugin})
|
||||
get_directory_property(PLUGIN_TARGETS DIRECTORY ${plugin} DEFINITION LOVR_PLUGIN_TARGETS)
|
||||
if(NOT PLUGIN_TARGETS)
|
||||
get_directory_property(PLUGIN_TARGETS DIRECTORY ${plugin} BUILDSYSTEM_TARGETS)
|
||||
endif()
|
||||
set_target_properties(${PLUGIN_TARGETS} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/plugins/lib")
|
||||
list(APPEND ALL_PLUGIN_TARGETS ${PLUGIN_TARGETS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(LOVR_SRC
|
||||
|
@ -344,7 +321,6 @@ set_target_properties(lovr PROPERTIES C_VISIBILITY_PRESET hidden)
|
|||
set_target_properties(lovr PROPERTIES C_STANDARD 99)
|
||||
target_include_directories(lovr PRIVATE src src/modules)
|
||||
target_link_libraries(lovr
|
||||
${LOVR_ENET}
|
||||
${LOVR_GLFW}
|
||||
${LOVR_LUA}
|
||||
${LOVR_MSDF}
|
||||
|
@ -515,19 +491,24 @@ if(LOVR_ENABLE_TIMER)
|
|||
target_sources(lovr PRIVATE src/modules/timer/timer.c src/api/l_timer.c)
|
||||
endif()
|
||||
|
||||
if(LOVR_ENABLE_ENET)
|
||||
target_compile_definitions(lovr PRIVATE LOVR_ENABLE_ENET)
|
||||
target_sources(lovr PRIVATE src/lib/lua-enet/enet.c)
|
||||
endif()
|
||||
# Resources
|
||||
file(GLOB LOVR_RESOURCES "src/resources/*.ttf" "src/resources/*.json" "src/resources/*.lua")
|
||||
foreach(path ${LOVR_RESOURCES})
|
||||
|
||||
if(LOVR_ENABLE_JSON)
|
||||
target_compile_definitions(lovr PRIVATE LOVR_ENABLE_JSON)
|
||||
target_sources(lovr PRIVATE
|
||||
src/lib/lua-cjson/fpconv.c
|
||||
src/lib/lua-cjson/lua_cjson.c
|
||||
src/lib/lua-cjson/strbuf.c
|
||||
)
|
||||
endif()
|
||||
# Turn the absolute path into a C variable like src_resources_boot_lua
|
||||
file(RELATIVE_PATH identifier ${CMAKE_CURRENT_SOURCE_DIR} ${path})
|
||||
string(MAKE_C_IDENTIFIER ${identifier} identifier)
|
||||
|
||||
# Read the file and turn the bytes into comma-separated hex literals
|
||||
file(READ ${path} data HEX)
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," data ${data})
|
||||
|
||||
# Generate the output filename by adding .h to the input filename
|
||||
string(CONCAT output ${path} ".h")
|
||||
|
||||
# Write some xxd-compatible C code!
|
||||
file(WRITE ${output} "const unsigned char ${identifier}[] = {${data}};\nconst unsigned int ${identifier}_len = sizeof(${identifier});\n")
|
||||
endforeach()
|
||||
|
||||
# Platforms
|
||||
if(WIN32)
|
||||
|
@ -558,6 +539,9 @@ if(WIN32)
|
|||
move_dll(${LOVR_OPENAL})
|
||||
move_dll(${LOVR_OPENVR})
|
||||
move_dll(${LOVR_MSDF})
|
||||
foreach(target ${ALL_PLUGIN_TARGETS})
|
||||
move_dll(${target})
|
||||
endforeach()
|
||||
target_compile_definitions(lovr PRIVATE LOVR_GL)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(lovr objc)
|
||||
|
@ -580,6 +564,9 @@ elseif(APPLE)
|
|||
move_lib(${LOVR_OPENAL})
|
||||
move_lib(${LOVR_OPENVR})
|
||||
move_lib(${LOVR_MSDF})
|
||||
foreach(target ${ALL_PLUGIN_TARGETS})
|
||||
move_lib(${target})
|
||||
endforeach()
|
||||
|
||||
target_sources(lovr PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/lovr.icns")
|
||||
set_target_properties(lovr PROPERTIES
|
||||
|
@ -595,14 +582,14 @@ elseif(EMSCRIPTEN)
|
|||
target_sources(lovr PRIVATE src/core/os_web.c)
|
||||
target_compile_definitions(lovr PRIVATE LOVR_WEBGL)
|
||||
elseif(ANDROID)
|
||||
target_link_libraries(lovr log EGL GLESv3 android)
|
||||
target_link_libraries(lovr log EGL GLESv3 android dl)
|
||||
target_compile_definitions(lovr PRIVATE LOVR_GLES)
|
||||
target_include_directories(lovr PRIVATE "${ANDROID_NDK}/sources/android/native_app_glue")
|
||||
|
||||
# Dynamically linked targets output libraries in lib/<ABI> for easy including in apk with aapt
|
||||
# Dynamically linked targets output libraries in raw/lib/<ABI> for easy including in apk with aapt
|
||||
set_target_properties(
|
||||
lovr ${LOVR_ODE} ${LOVR_OPENAL}
|
||||
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib/${ANDROID_ABI}"
|
||||
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/raw/lib/${ANDROID_ABI}"
|
||||
)
|
||||
|
||||
if(LOVR_BUILD_EXE)
|
||||
|
@ -615,7 +602,7 @@ elseif(ANDROID)
|
|||
endif()
|
||||
|
||||
# Flavor-specific config:
|
||||
# - Imported targets need to have their libraries manually copied to lib/<ABI>
|
||||
# - Imported targets need to have their libraries manually copied to raw/lib/<ABI>
|
||||
# - Figure out which Java class (Activity) and AndroidManifest.xml to use
|
||||
# - Oculus uses the regular android os layer, pico implements its own in the headset backend
|
||||
# - Some of the Pico SDK is in a jar that has to be added to the classpath and dx invocation
|
||||
|
@ -625,20 +612,20 @@ elseif(ANDROID)
|
|||
set(ACTIVITY "openxr")
|
||||
target_sources(lovr PRIVATE src/core/os_android.c)
|
||||
get_target_property(OPENXR_LIB ${LOVR_OPENXR} IMPORTED_LOCATION)
|
||||
file(COPY ${OPENXR_LIB} DESTINATION lib/${ANDROID_ABI})
|
||||
file(COPY ${OPENXR_LIB} DESTINATION raw/lib/${ANDROID_ABI})
|
||||
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
|
||||
elseif(LOVR_USE_VRAPI)
|
||||
set(MANIFEST "oculus")
|
||||
set(ACTIVITY "vrapi")
|
||||
target_sources(lovr PRIVATE src/core/os_android.c)
|
||||
get_target_property(VRAPI_LIB ${LOVR_VRAPI} IMPORTED_LOCATION)
|
||||
file(COPY ${VRAPI_LIB} DESTINATION lib/${ANDROID_ABI})
|
||||
file(COPY ${VRAPI_LIB} DESTINATION raw/lib/${ANDROID_ABI})
|
||||
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
|
||||
elseif(LOVR_USE_PICO)
|
||||
set(MANIFEST "pico")
|
||||
set(ACTIVITY "pico")
|
||||
get_target_property(PICO_LIB ${LOVR_PICO} IMPORTED_LOCATION)
|
||||
file(COPY ${PICO_LIB} DESTINATION lib/${ANDROID_ABI})
|
||||
file(COPY ${PICO_LIB} DESTINATION raw/lib/${ANDROID_ABI})
|
||||
set(EXTRA_JAR "${LOVR_PICO_PATH}/classes.jar")
|
||||
if(WIN32)
|
||||
set(ANDROID_CLASSPATH "${ANDROID_JAR};${EXTRA_JAR}")
|
||||
|
@ -660,28 +647,35 @@ elseif(ANDROID)
|
|||
COMMAND ${CMAKE_COMMAND} -E copy "${ANDROID_MANIFEST}" AndroidManifest.xml
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ACTIVITY}.java Activity.java
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE} -classpath "${ANDROID_CLASSPATH}" -d . Activity.java
|
||||
COMMAND ${ANDROID_TOOLS}/dx --dex --output classes.dex ${EXTRA_JAR} org/lovr/app/Activity.class
|
||||
COMMAND ${ANDROID_TOOLS}/dx --dex --output raw/classes.dex ${EXTRA_JAR} org/lovr/app/Activity.class
|
||||
COMMAND
|
||||
${ANDROID_TOOLS}/aapt
|
||||
package -f
|
||||
-0 so
|
||||
-M AndroidManifest.xml
|
||||
-I ${ANDROID_JAR}
|
||||
-F lovr.unaligned.apk
|
||||
${ANDROID_ASSETS}
|
||||
COMMAND
|
||||
${ANDROID_TOOLS}/aapt
|
||||
add -f lovr.unaligned.apk
|
||||
classes.dex lib/${ANDROID_ABI}/*.so
|
||||
COMMAND ${ANDROID_TOOLS}/zipalign -f 4 lovr.unaligned.apk lovr.unsigned.apk
|
||||
raw
|
||||
COMMAND ${ANDROID_TOOLS}/zipalign -f -p 4 lovr.unaligned.apk lovr.unsigned.apk
|
||||
COMMAND ${ANDROID_TOOLS}/apksigner
|
||||
sign
|
||||
--ks ${ANDROID_KEYSTORE}
|
||||
${ANDROID_APKSIGNER_KEYSTORE_PASS} ${ANDROID_KEYSTORE_PASS}
|
||||
--in lovr.unsigned.apk
|
||||
--out lovr.apk
|
||||
COMMAND ${CMAKE_COMMAND} -E remove lovr.unaligned.apk lovr.unsigned.apk AndroidManifest.xml Activity.java classes.dex
|
||||
COMMAND ${CMAKE_COMMAND} -E remove lovr.unaligned.apk lovr.unsigned.apk AndroidManifest.xml Activity.java
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory org
|
||||
)
|
||||
|
||||
# Copy plugin libraries to lib folder before packaging APK
|
||||
foreach(target ${ALL_PLUGIN_TARGETS})
|
||||
add_custom_command(TARGET lovr POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
$<TARGET_FILE:${target}>
|
||||
raw/lib/${ANDROID_ABI}/$<TARGET_FILE_NAME:${target}>
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
if(LOVR_USE_LINUX_EGL)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit a84c120eff13d2fa3eadb41ef7afe0f7819f4d6c
|
|
@ -169,8 +169,7 @@ static int l_lovrFilesystemGetRealDirectory(lua_State* L) {
|
|||
|
||||
static int l_lovrFilesystemGetRequirePath(lua_State* L) {
|
||||
lua_pushstring(L, lovrFilesystemGetRequirePath());
|
||||
lua_pushstring(L, lovrFilesystemGetCRequirePath());
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrFilesystemGetSaveDirectory(lua_State* L) {
|
||||
|
@ -298,8 +297,7 @@ static int l_lovrFilesystemSetIdentity(lua_State* L) {
|
|||
}
|
||||
|
||||
static int l_lovrFilesystemSetRequirePath(lua_State* L) {
|
||||
if (lua_type(L, 1) == LUA_TSTRING) lovrFilesystemSetRequirePath(lua_tostring(L, 1));
|
||||
if (lua_type(L, 2) == LUA_TSTRING) lovrFilesystemSetCRequirePath(lua_tostring(L, 2));
|
||||
lovrFilesystemSetRequirePath(luaL_checkstring(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -404,70 +402,67 @@ static int libLoader(lua_State* L) {
|
|||
const char* module = lua_tostring(L, 1);
|
||||
const char* hyphen = strchr(module, '-');
|
||||
const char* symbol = hyphen ? hyphen + 1 : module;
|
||||
const char* p = lovrFilesystemGetCRequirePath();
|
||||
|
||||
char filename[1024];
|
||||
char* f = filename;
|
||||
size_t n = sizeof(filename);
|
||||
char path[1024];
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
while (1) {
|
||||
if (*p == ';' || *p == '\0') {
|
||||
*f = '\0';
|
||||
// On Android, load libraries directly from the apk by passing a path like this to the linker:
|
||||
// /path/to/app.apk!/lib/arm64-v8a/lib.so
|
||||
// On desktop systems, look for libraries next to the executable
|
||||
#ifdef __ANDROID__
|
||||
const char* source = lovrFilesystemGetSource();
|
||||
size_t length = strlen(source);
|
||||
memcpy(path, source, length);
|
||||
|
||||
if (lovrFilesystemIsFile(filename)) {
|
||||
lua_getfield(L, -1, "loadlib");
|
||||
|
||||
// Synthesize the absolute path to the library on disk
|
||||
luaL_Buffer buffer;
|
||||
luaL_buffinit(L, &buffer);
|
||||
luaL_addstring(&buffer, lovrFilesystemGetRealDirectory(filename));
|
||||
luaL_addchar(&buffer, LOVR_PATH_SEP);
|
||||
luaL_addstring(&buffer, filename);
|
||||
luaL_pushresult(&buffer);
|
||||
|
||||
// Synthesize the symbol to load: luaopen_ followed by the module name with dots converted
|
||||
// to underscores, starting after the first hyphen (if there is one).
|
||||
luaL_buffinit(L, &buffer);
|
||||
luaL_addstring(&buffer, "luaopen_");
|
||||
for (const char* s = symbol; *s; s++) {
|
||||
luaL_addchar(&buffer, *s == '.' ? '_' : *s);
|
||||
}
|
||||
luaL_pushresult(&buffer);
|
||||
|
||||
// Finally call package.loadlib with the library path and symbol name
|
||||
lua_call(L, 2, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
break;
|
||||
} else {
|
||||
p++;
|
||||
f = filename;
|
||||
n = sizeof(filename);
|
||||
}
|
||||
} else if (*p == '?') {
|
||||
for (const char* m = module; n && *m; n--, m++) {
|
||||
*f++ = *m == '.' ? LOVR_PATH_SEP : *m;
|
||||
}
|
||||
p++;
|
||||
|
||||
if (*p == '?') {
|
||||
for (const char* e = extension; n && *e; n--, e++) {
|
||||
*f++ = *e;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
} else {
|
||||
*f++ = *p++;
|
||||
n--;
|
||||
}
|
||||
|
||||
lovrAssert(n > 0, "Tried to require a filename that was too long (%s)", module);
|
||||
const char* subpath = "!/lib/arm64-v8a/";
|
||||
size_t subpathLength = strlen(subpath);
|
||||
char* p = path + length;
|
||||
if (length + subpathLength >= sizeof(path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
memcpy(p, subpath, subpathLength);
|
||||
length += subpathLength;
|
||||
p += subpathLength;
|
||||
#else
|
||||
size_t length = lovrFilesystemGetExecutablePath(path, sizeof(path));
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* slash = strrchr(path, LOVR_PATH_SEP);
|
||||
char* p = slash ? slash + 1 : path;
|
||||
length = p - path;
|
||||
#endif
|
||||
|
||||
for (const char* m = module; *m && length < sizeof(path); m++, length++) {
|
||||
*p++ = *m == '.' ? LOVR_PATH_SEP : *m;
|
||||
}
|
||||
|
||||
for (const char* e = extension; *e && length < sizeof(path); e++, length++) {
|
||||
*p++ = *e;
|
||||
}
|
||||
|
||||
if (length >= sizeof(path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
lua_getfield(L, -1, "loadlib");
|
||||
lua_pushlstring(L, path, length);
|
||||
|
||||
// Synthesize luaopen_<module> symbol
|
||||
luaL_Buffer buffer;
|
||||
luaL_buffinit(L, &buffer);
|
||||
luaL_addstring(&buffer, "luaopen_");
|
||||
for (const char* s = symbol; *s; s++) {
|
||||
luaL_addchar(&buffer, *s == '.' ? '_' : *s);
|
||||
}
|
||||
luaL_pushresult(&buffer);
|
||||
|
||||
lua_call(L, 2, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_lovr_filesystem(lua_State* L) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include "api.h"
|
||||
#include "core/os.h"
|
||||
#include "core/util.h"
|
||||
#include "lib/lua-cjson/lua_cjson.h"
|
||||
#include "lib/lua-enet/enet.h"
|
||||
|
||||
const luaL_Reg lovrModules[] = {
|
||||
{ "lovr", luaopen_lovr },
|
||||
|
@ -35,12 +33,6 @@ const luaL_Reg lovrModules[] = {
|
|||
#endif
|
||||
#ifdef LOVR_ENABLE_TIMER
|
||||
{ "lovr.timer", luaopen_lovr_timer },
|
||||
#endif
|
||||
#ifdef LOVR_ENABLE_JSON
|
||||
{ "cjson", luaopen_cjson },
|
||||
#endif
|
||||
#ifdef LOVR_ENABLE_ENET
|
||||
{ "enet", luaopen_enet },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,205 +0,0 @@
|
|||
/* fpconv - Floating point conversion routines
|
||||
*
|
||||
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||
* with locale support will break when the decimal separator is a comma.
|
||||
*
|
||||
* fpconv_* will around these issues with a translation buffer if required.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpconv.h"
|
||||
|
||||
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||
* process and doesn't change after initialisation.
|
||||
*
|
||||
* This avoids the need for per thread storage or expensive checks
|
||||
* for call. */
|
||||
static char locale_decimal_point = '.';
|
||||
|
||||
/* In theory multibyte decimal_points are possible, but
|
||||
* Lua CJSON only supports UTF-8 and known locales only have
|
||||
* single byte decimal points ([.,]).
|
||||
*
|
||||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||
* not supported on some platforms. Use sprintf() instead - if the
|
||||
* locale does change, at least Lua CJSON won't crash. */
|
||||
static void fpconv_update_locale()
|
||||
{
|
||||
char buf[8];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%g", 0.5);
|
||||
|
||||
/* Failing this test might imply the platform has a buggy dtoa
|
||||
* implementation or wide characters */
|
||||
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||
fprintf(stderr, "Error: wide characters found or printf() bug.");
|
||||
abort();
|
||||
}
|
||||
|
||||
locale_decimal_point = buf[1];
|
||||
}
|
||||
|
||||
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||
*
|
||||
* Used to find the probable end of a number. It doesn't matter if
|
||||
* invalid characters are counted - strtod() will find the valid
|
||||
* number if it exists. The risk is that slightly more memory might
|
||||
* be allocated before a parse error occurs. */
|
||||
static inline int valid_number_character(char ch)
|
||||
{
|
||||
char lower_ch;
|
||||
|
||||
if ('0' <= ch && ch <= '9')
|
||||
return 1;
|
||||
if (ch == '-' || ch == '+' || ch == '.')
|
||||
return 1;
|
||||
|
||||
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||
lower_ch = ch | 0x20;
|
||||
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the size of the buffer required for a strtod locale
|
||||
* conversion. */
|
||||
static int strtod_buffer_size(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
|
||||
while (valid_number_character(*p))
|
||||
p++;
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||
double fpconv_strtod(const char *nptr, char **endptr)
|
||||
{
|
||||
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||
char *buf, *endbuf, *dp;
|
||||
int buflen;
|
||||
double value;
|
||||
|
||||
/* System strtod() is fine when decimal point is '.' */
|
||||
if (locale_decimal_point == '.')
|
||||
return strtod(nptr, endptr);
|
||||
|
||||
buflen = strtod_buffer_size(nptr);
|
||||
if (!buflen) {
|
||||
/* No valid characters found, standard strtod() return */
|
||||
*endptr = (char *)nptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Duplicate number into buffer */
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||
/* Handle unusually large numbers */
|
||||
buf = (char *)malloc(buflen + 1);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Out of memory");
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* This is the common case.. */
|
||||
buf = localbuf;
|
||||
}
|
||||
memcpy(buf, nptr, buflen);
|
||||
buf[buflen] = 0;
|
||||
|
||||
/* Update decimal point character if found */
|
||||
dp = strchr(buf, '.');
|
||||
if (dp)
|
||||
*dp = locale_decimal_point;
|
||||
|
||||
value = strtod(buf, &endbuf);
|
||||
*endptr = (char *)&nptr[endbuf - buf];
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||
free(buf);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* "fmt" must point to a buffer of at least 6 characters */
|
||||
static void set_number_format(char *fmt, int precision)
|
||||
{
|
||||
int d1, d2, i;
|
||||
|
||||
assert(1 <= precision && precision <= 14);
|
||||
|
||||
/* Create printf format (%.14g) from precision */
|
||||
d1 = precision / 10;
|
||||
d2 = precision % 10;
|
||||
fmt[0] = '%';
|
||||
fmt[1] = '.';
|
||||
i = 2;
|
||||
if (d1) {
|
||||
fmt[i++] = '0' + d1;
|
||||
}
|
||||
fmt[i++] = '0' + d2;
|
||||
fmt[i++] = 'g';
|
||||
fmt[i] = 0;
|
||||
}
|
||||
|
||||
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||
int fpconv_g_fmt(char *str, double num, int precision)
|
||||
{
|
||||
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||
char fmt[6];
|
||||
int len;
|
||||
char *b;
|
||||
|
||||
set_number_format(fmt, precision);
|
||||
|
||||
/* Pass through when decimal point character is dot. */
|
||||
if (locale_decimal_point == '.')
|
||||
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* Copy into target location. Translate decimal point if required */
|
||||
b = buf;
|
||||
do {
|
||||
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||
} while(*b++);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void fpconv_init()
|
||||
{
|
||||
fpconv_update_locale();
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
|
@ -1,22 +0,0 @@
|
|||
/* Lua CJSON floating point conversion routines */
|
||||
|
||||
/* Buffer required to store the largest string representation of a double.
|
||||
*
|
||||
* Longest double printed with %.14g is 21 characters long:
|
||||
* -1.7976931348623e+308 */
|
||||
# define FPCONV_G_FMT_BUFSIZE 32
|
||||
|
||||
#ifdef USE_INTERNAL_FPCONV
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
/* Do nothing - not required */
|
||||
}
|
||||
#else
|
||||
extern void fpconv_init();
|
||||
#endif
|
||||
|
||||
extern int fpconv_g_fmt(char*, double, int);
|
||||
extern double fpconv_strtod(const char*, char**);
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
|||
#include "api/api.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
int luaopen_cjson(lua_State *l);
|
|
@ -1,252 +0,0 @@
|
|||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
static void die(const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, fmt, arg);
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (len <= 0)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
|
||||
s->buf = (char *)malloc(size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
s = (strbuf_t*)malloc(sizeof(strbuf_t));
|
||||
if (!s)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_init(s, len);
|
||||
|
||||
/* Dynamic strbuf allocation / deallocation */
|
||||
s->dynamic = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1)
|
||||
die("BUG: Invalid string increment");
|
||||
|
||||
s->increment = increment;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "strbuf(%p) reallocs: %d, length: %d, size: %d\n",
|
||||
(void*) s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||
* be called any number of times strbuf_init() */
|
||||
void strbuf_free(strbuf_t *s)
|
||||
{
|
||||
debug_stats(s);
|
||||
|
||||
if (s->buf) {
|
||||
free(s->buf);
|
||||
s->buf = NULL;
|
||||
}
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
debug_stats(s);
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
|
||||
buf = s->buf;
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
|
||||
if (len <= 0)
|
||||
die("BUG: Invalid strbuf length requested");
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
}
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
{
|
||||
int newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%p) resize: %d => %d\n",
|
||||
(void*) s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = (char *)realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
for (i = 0; str[i]; i++) {
|
||||
if (space < 1) {
|
||||
strbuf_resize(s, s->length + 1);
|
||||
space = strbuf_empty_length(s);
|
||||
}
|
||||
|
||||
s->buf[s->length] = str[i];
|
||||
s->length++;
|
||||
space--;
|
||||
}
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
int empty_len;
|
||||
int t;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (t = 0; ; t++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (t > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
|
@ -1,154 +0,0 @@
|
|||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
} strbuf_t;
|
||||
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern void strbuf_init(strbuf_t *s, int len);
|
||||
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
|
||||
/* Management */
|
||||
extern void strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
|
||||
/* Reset string for before use */
|
||||
static inline void strbuf_reset(strbuf_t *s)
|
||||
{
|
||||
s->length = 0;
|
||||
}
|
||||
|
||||
static inline int strbuf_allocated(strbuf_t *s)
|
||||
{
|
||||
return s->buf != NULL;
|
||||
}
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
}
|
||||
|
||||
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
{
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, 1);
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
{
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
{
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
return s->buf;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
|
@ -1,808 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2014 by Leaf Corcoran
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// For lua5.2 support, instead we could replace all the luaL_register's with whatever
|
||||
// lua5.2's equivalent function is, but this is easier so whatever.
|
||||
#define LUA_COMPAT_MODULE
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
#include <enet/enet.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define check_host(l, idx)\
|
||||
*(ENetHost**)luaL_checkudata(l, idx, "enet_host")
|
||||
|
||||
#define check_peer(l, idx)\
|
||||
*(ENetPeer**)luaL_checkudata(l, idx, "enet_peer")
|
||||
|
||||
/**
|
||||
* Parse address string, eg:
|
||||
* *:5959
|
||||
* 127.0.0.1:*
|
||||
* website.com:8080
|
||||
*/
|
||||
static void parse_address(lua_State *l, const char *addr_str, ENetAddress *address) {
|
||||
int host_i = 0, port_i = 0;
|
||||
char host_str[128] = {0};
|
||||
char port_str[32] = {0};
|
||||
int scanning_port = 0;
|
||||
|
||||
char *c = (char *)addr_str;
|
||||
|
||||
while (*c != 0) {
|
||||
if (host_i >= 128 || port_i >= 32 ) luaL_error(l, "Hostname too long");
|
||||
if (scanning_port) {
|
||||
port_str[port_i++] = *c;
|
||||
} else {
|
||||
if (*c == ':') {
|
||||
scanning_port = 1;
|
||||
} else {
|
||||
host_str[host_i++] = *c;
|
||||
}
|
||||
}
|
||||
c++;
|
||||
}
|
||||
host_str[host_i] = '\0';
|
||||
port_str[port_i] = '\0';
|
||||
|
||||
if (host_i == 0) luaL_error(l, "Failed to parse address");
|
||||
if (port_i == 0) luaL_error(l, "Missing port in address");
|
||||
|
||||
if (strcmp("*", host_str) == 0) {
|
||||
address->host = ENET_HOST_ANY;
|
||||
} else {
|
||||
if (enet_address_set_host(address, host_str) != 0) {
|
||||
luaL_error(l, "Failed to resolve host name");
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp("*", port_str) == 0) {
|
||||
address->port = ENET_PORT_ANY;
|
||||
} else {
|
||||
address->port = atoi(port_str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of a given peer for which we only have the pointer.
|
||||
*/
|
||||
size_t find_peer_index (lua_State *l, ENetHost *enet_host, ENetPeer *peer) {
|
||||
size_t peer_index;
|
||||
for (peer_index = 0; peer_index < enet_host->peerCount; peer_index++) {
|
||||
if (peer == &(enet_host->peers[peer_index]))
|
||||
return peer_index;
|
||||
}
|
||||
|
||||
luaL_error (l, "enet: could not find peer id!");
|
||||
|
||||
return peer_index;
|
||||
}
|
||||
|
||||
static void push_peer(lua_State *l, ENetPeer *peer) {
|
||||
// try to find in peer table
|
||||
lua_getfield(l, LUA_REGISTRYINDEX, "enet_peers");
|
||||
lua_pushlightuserdata(l, peer);
|
||||
lua_gettable(l, -2);
|
||||
|
||||
if (lua_isnil(l, -1)) {
|
||||
// printf("creating new peer\n");
|
||||
lua_pop(l, 1);
|
||||
|
||||
*(ENetPeer**)lua_newuserdata(l, sizeof(void*)) = peer;
|
||||
luaL_getmetatable(l, "enet_peer");
|
||||
lua_setmetatable(l, -2);
|
||||
|
||||
lua_pushlightuserdata(l, peer);
|
||||
lua_pushvalue(l, -2);
|
||||
|
||||
lua_settable(l, -4);
|
||||
}
|
||||
lua_remove(l, -2); // remove enet_peers
|
||||
}
|
||||
|
||||
static void push_event(lua_State *l, ENetEvent *event) {
|
||||
lua_newtable(l); // event table
|
||||
|
||||
if (event->peer) {
|
||||
push_peer(l, event->peer);
|
||||
lua_setfield(l, -2, "peer");
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
lua_pushinteger(l, event->data);
|
||||
lua_setfield(l, -2, "data");
|
||||
|
||||
lua_pushstring(l, "connect");
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
lua_pushinteger(l, event->data);
|
||||
lua_setfield(l, -2, "data");
|
||||
|
||||
lua_pushstring(l, "disconnect");
|
||||
break;
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
lua_pushlstring(l, (const char *)event->packet->data, event->packet->dataLength);
|
||||
lua_setfield(l, -2, "data");
|
||||
|
||||
lua_pushinteger(l, event->channelID);
|
||||
lua_setfield(l, -2, "channel");
|
||||
|
||||
lua_pushstring(l, "receive");
|
||||
|
||||
enet_packet_destroy(event->packet);
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
lua_pushstring(l, "none");
|
||||
break;
|
||||
}
|
||||
|
||||
lua_setfield(l, -2, "type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a packet off the stack as a string
|
||||
* idx is position of string
|
||||
*/
|
||||
static ENetPacket *read_packet(lua_State *l, int idx, enet_uint8 *channel_id) {
|
||||
size_t size;
|
||||
int argc = lua_gettop(l);
|
||||
const void *data = luaL_checklstring(l, idx, &size);
|
||||
ENetPacket *packet;
|
||||
|
||||
enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
*channel_id = 0;
|
||||
|
||||
if (argc >= idx+2 && !lua_isnil(l, idx+2)) {
|
||||
const char *flag_str = luaL_checkstring(l, idx+2);
|
||||
if (strcmp("unsequenced", flag_str) == 0) {
|
||||
flags = ENET_PACKET_FLAG_UNSEQUENCED;
|
||||
} else if (strcmp("reliable", flag_str) == 0) {
|
||||
flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
} else if (strcmp("unreliable", flag_str) == 0) {
|
||||
flags = 0;
|
||||
} else {
|
||||
luaL_error(l, "Unknown packet flag: %s", flag_str);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= idx+1 && !lua_isnil(l, idx+1)) {
|
||||
*channel_id = luaL_checkint(l, idx+1);
|
||||
}
|
||||
|
||||
packet = enet_packet_create(data, size, flags);
|
||||
if (packet == NULL) {
|
||||
luaL_error(l, "Failed to create packet");
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new host
|
||||
* Args:
|
||||
* address (nil for client)
|
||||
* [peer_count = 64]
|
||||
* [channel_count = 1]
|
||||
* [in_bandwidth = 0]
|
||||
* [out_bandwidth = 0]
|
||||
*/
|
||||
static int host_create(lua_State *l) {
|
||||
ENetHost *host;
|
||||
size_t peer_count = 64, channel_count = 1;
|
||||
enet_uint32 in_bandwidth = 0, out_bandwidth = 0;
|
||||
|
||||
int have_address = 1;
|
||||
ENetAddress address;
|
||||
|
||||
if (lua_gettop(l) == 0 || lua_isnil(l, 1)) {
|
||||
have_address = 0;
|
||||
} else {
|
||||
parse_address(l, luaL_checkstring(l, 1), &address);
|
||||
}
|
||||
|
||||
switch (lua_gettop(l)) {
|
||||
case 5:
|
||||
if (!lua_isnil(l, 5)) out_bandwidth = luaL_checkint(l, 5);
|
||||
case 4:
|
||||
if (!lua_isnil(l, 4)) in_bandwidth = luaL_checkint(l, 4);
|
||||
case 3:
|
||||
if (!lua_isnil(l, 3)) channel_count = luaL_checkint(l, 3);
|
||||
case 2:
|
||||
if (!lua_isnil(l, 2)) peer_count = luaL_checkint(l, 2);
|
||||
}
|
||||
|
||||
// printf("host create, peers=%d, channels=%d, in=%d, out=%d\n",
|
||||
// peer_count, channel_count, in_bandwidth, out_bandwidth);
|
||||
host = enet_host_create(have_address ? &address : NULL, peer_count,
|
||||
channel_count, in_bandwidth, out_bandwidth);
|
||||
|
||||
if (host == NULL) {
|
||||
lua_pushnil (l);
|
||||
lua_pushstring(l, "enet: failed to create host (already listening?)");
|
||||
return 2;
|
||||
}
|
||||
|
||||
*(ENetHost**)lua_newuserdata(l, sizeof(void*)) = host;
|
||||
luaL_getmetatable(l, "enet_host");
|
||||
lua_setmetatable(l, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int linked_version(lua_State *l) {
|
||||
lua_pushfstring(l, "%d.%d.%d",
|
||||
ENET_VERSION_GET_MAJOR(enet_linked_version()),
|
||||
ENET_VERSION_GET_MINOR(enet_linked_version()),
|
||||
ENET_VERSION_GET_PATCH(enet_linked_version()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serice a host
|
||||
* Args:
|
||||
* timeout
|
||||
*
|
||||
* Return
|
||||
* nil on no event
|
||||
* an event table on event
|
||||
*/
|
||||
static int host_service(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
ENetEvent event;
|
||||
int timeout = 0, out;
|
||||
|
||||
if (lua_gettop(l) > 1)
|
||||
timeout = luaL_checkint(l, 2);
|
||||
|
||||
out = enet_host_service(host, &event, timeout);
|
||||
if (out == 0) return 0;
|
||||
if (out < 0) return luaL_error(l, "Error during service");
|
||||
|
||||
push_event(l, &event);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a single event if available
|
||||
*/
|
||||
static int host_check_events(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
ENetEvent event;
|
||||
int out = enet_host_check_events(host, &event);
|
||||
if (out == 0) return 0;
|
||||
if (out < 0) return luaL_error(l, "Error checking event");
|
||||
|
||||
push_event(l, &event);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables an adaptive order-2 PPM range coder for the transmitted data of
|
||||
* all peers.
|
||||
*/
|
||||
static int host_compress_with_range_coder(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
int result = enet_host_compress_with_range_coder (host);
|
||||
if (result == 0) {
|
||||
lua_pushboolean (l, 1);
|
||||
} else {
|
||||
lua_pushboolean (l, 0);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a host to an address
|
||||
* Args:
|
||||
* the address
|
||||
* [channel_count = 1]
|
||||
* [data = 0]
|
||||
*/
|
||||
static int host_connect(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
ENetAddress address;
|
||||
ENetPeer *peer;
|
||||
|
||||
enet_uint32 data = 0;
|
||||
size_t channel_count = 1;
|
||||
|
||||
parse_address(l, luaL_checkstring(l, 2), &address);
|
||||
|
||||
switch (lua_gettop(l)) {
|
||||
case 4:
|
||||
if (!lua_isnil(l, 4)) data = luaL_checkint(l, 4);
|
||||
case 3:
|
||||
if (!lua_isnil(l, 3)) channel_count = luaL_checkint(l, 3);
|
||||
}
|
||||
|
||||
// printf("host connect, channels=%d, data=%d\n", channel_count, data);
|
||||
peer = enet_host_connect(host, &address, channel_count, data);
|
||||
|
||||
if (peer == NULL) {
|
||||
return luaL_error(l, "Failed to create peer");
|
||||
}
|
||||
|
||||
push_peer(l, peer);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int host_flush(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
enet_host_flush(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host_broadcast(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
enet_uint8 channel_id;
|
||||
ENetPacket *packet = read_packet(l, 2, &channel_id);
|
||||
enet_host_broadcast(host, channel_id, packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Args: limit:number
|
||||
static int host_channel_limit(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
int limit = luaL_checkint(l, 2);
|
||||
enet_host_channel_limit(host, limit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host_bandwidth_limit(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
enet_uint32 in_bandwidth = luaL_checkint(l, 2);
|
||||
enet_uint32 out_bandwidth = luaL_checkint(l, 2);
|
||||
enet_host_bandwidth_limit(host, in_bandwidth, out_bandwidth);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host_get_socket_address(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
ENetAddress address;
|
||||
enet_socket_get_address (host->socket, &address);
|
||||
|
||||
lua_pushfstring(l, "%d.%d.%d.%d:%d",
|
||||
((address.host) & 0xFF),
|
||||
((address.host >> 8) & 0xFF),
|
||||
((address.host >> 16) & 0xFF),
|
||||
(address.host >> 24& 0xFF),
|
||||
address.port);
|
||||
|
||||
return 1;
|
||||
}
|
||||
static int host_total_sent_data(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
lua_pushinteger (l, host->totalSentData);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int host_total_received_data(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
lua_pushinteger (l, host->totalReceivedData);
|
||||
|
||||
return 1;
|
||||
}
|
||||
static int host_service_time(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
lua_pushinteger (l, host->serviceTime);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int host_peer_count(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
lua_pushinteger (l, host->peerCount);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int host_get_peer(lua_State *l) {
|
||||
ENetHost *host = check_host(l, 1);
|
||||
if (!host) {
|
||||
return luaL_error(l, "Tried to index a nil host!");
|
||||
}
|
||||
|
||||
size_t peer_index = (size_t) luaL_checkint(l, 2) - 1;
|
||||
|
||||
if (peer_index >= host->peerCount) {
|
||||
luaL_argerror (l, 2, "Invalid peer index");
|
||||
}
|
||||
|
||||
ENetPeer *peer = &(host->peers[peer_index]);
|
||||
|
||||
push_peer (l, peer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int host_gc(lua_State *l) {
|
||||
// We have to manually grab the userdata so that we can set it to NULL.
|
||||
ENetHost** host = luaL_checkudata(l, 1, "enet_host");
|
||||
// We don't want to crash by destroying a non-existant host.
|
||||
if (*host) {
|
||||
enet_host_destroy(*host);
|
||||
}
|
||||
*host = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_tostring(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
char host_str[128];
|
||||
enet_address_get_host_ip(&peer->address, host_str, 128);
|
||||
|
||||
lua_pushstring(l, host_str);
|
||||
lua_pushstring(l, ":");
|
||||
lua_pushinteger(l, peer->address.port);
|
||||
lua_concat(l, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_ping(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
enet_peer_ping(peer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_throttle_configure(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint32 interval = luaL_checkint(l, 2);
|
||||
enet_uint32 acceleration = luaL_checkint(l, 3);
|
||||
enet_uint32 deceleration = luaL_checkint(l, 4);
|
||||
|
||||
enet_peer_throttle_configure(peer, interval, acceleration, deceleration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_round_trip_time(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
if (lua_gettop(l) > 1) {
|
||||
enet_uint32 round_trip_time = luaL_checkint(l, 2);
|
||||
peer->roundTripTime = round_trip_time;
|
||||
}
|
||||
|
||||
lua_pushinteger (l, peer->roundTripTime);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_last_round_trip_time(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
if (lua_gettop(l) > 1) {
|
||||
enet_uint32 round_trip_time = luaL_checkint(l, 2);
|
||||
peer->lastRoundTripTime = round_trip_time;
|
||||
}
|
||||
lua_pushinteger (l, peer->lastRoundTripTime);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_ping_interval(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
if (lua_gettop(l) > 1) {
|
||||
enet_uint32 interval = luaL_checkint(l, 2);
|
||||
enet_peer_ping_interval (peer, interval);
|
||||
}
|
||||
|
||||
lua_pushinteger (l, peer->pingInterval);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_timeout(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint32 timeout_limit = 0;
|
||||
enet_uint32 timeout_minimum = 0;
|
||||
enet_uint32 timeout_maximum = 0;
|
||||
|
||||
switch (lua_gettop(l)) {
|
||||
case 4:
|
||||
if (!lua_isnil(l, 4)) timeout_maximum = luaL_checkint(l, 4);
|
||||
case 3:
|
||||
if (!lua_isnil(l, 3)) timeout_minimum = luaL_checkint(l, 3);
|
||||
case 2:
|
||||
if (!lua_isnil(l, 2)) timeout_limit = luaL_checkint(l, 2);
|
||||
}
|
||||
|
||||
enet_peer_timeout (peer, timeout_limit, timeout_minimum, timeout_maximum);
|
||||
|
||||
lua_pushinteger (l, peer->timeoutLimit);
|
||||
lua_pushinteger (l, peer->timeoutMinimum);
|
||||
lua_pushinteger (l, peer->timeoutMaximum);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int peer_disconnect(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0;
|
||||
enet_peer_disconnect(peer, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_disconnect_now(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0;
|
||||
enet_peer_disconnect_now(peer, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_disconnect_later(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0;
|
||||
enet_peer_disconnect_later(peer, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_index(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
size_t peer_index = find_peer_index (l, peer->host, peer);
|
||||
lua_pushinteger (l, peer_index + 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_state(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
switch (peer->state) {
|
||||
case (ENET_PEER_STATE_DISCONNECTED):
|
||||
lua_pushstring (l, "disconnected");
|
||||
break;
|
||||
case (ENET_PEER_STATE_CONNECTING):
|
||||
lua_pushstring (l, "connecting");
|
||||
break;
|
||||
case (ENET_PEER_STATE_ACKNOWLEDGING_CONNECT):
|
||||
lua_pushstring (l, "acknowledging_connect");
|
||||
break;
|
||||
case (ENET_PEER_STATE_CONNECTION_PENDING):
|
||||
lua_pushstring (l, "connection_pending");
|
||||
break;
|
||||
case (ENET_PEER_STATE_CONNECTION_SUCCEEDED):
|
||||
lua_pushstring (l, "connection_succeeded");
|
||||
break;
|
||||
case (ENET_PEER_STATE_CONNECTED):
|
||||
lua_pushstring (l, "connected");
|
||||
break;
|
||||
case (ENET_PEER_STATE_DISCONNECT_LATER):
|
||||
lua_pushstring (l, "disconnect_later");
|
||||
break;
|
||||
case (ENET_PEER_STATE_DISCONNECTING):
|
||||
lua_pushstring (l, "disconnecting");
|
||||
break;
|
||||
case (ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT):
|
||||
lua_pushstring (l, "acknowledging_disconnect");
|
||||
break;
|
||||
case (ENET_PEER_STATE_ZOMBIE):
|
||||
lua_pushstring (l, "zombie");
|
||||
break;
|
||||
default:
|
||||
lua_pushstring (l, "unknown");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int peer_connect_id(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
lua_pushinteger (l, peer->connectID);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int peer_reset(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
enet_peer_reset(peer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peer_receive(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
ENetPacket *packet;
|
||||
enet_uint8 channel_id = 0;
|
||||
|
||||
if (lua_gettop(l) > 1) {
|
||||
channel_id = luaL_checkint(l, 2);
|
||||
}
|
||||
|
||||
packet = enet_peer_receive(peer, &channel_id);
|
||||
if (packet == NULL) return 0;
|
||||
|
||||
lua_pushlstring(l, (const char *)packet->data, packet->dataLength);
|
||||
lua_pushinteger(l, channel_id);
|
||||
|
||||
enet_packet_destroy(packet);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a lua string to a peer
|
||||
* Args:
|
||||
* packet data, string
|
||||
* channel id
|
||||
* flags ["reliable", nil]
|
||||
*
|
||||
*/
|
||||
static int peer_send(lua_State *l) {
|
||||
ENetPeer *peer = check_peer(l, 1);
|
||||
|
||||
enet_uint8 channel_id;
|
||||
ENetPacket *packet = read_packet(l, 2, &channel_id);
|
||||
|
||||
// printf("sending, channel_id=%d\n", channel_id);
|
||||
enet_peer_send(peer, channel_id, packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg enet_funcs [] = {
|
||||
{"host_create", host_create},
|
||||
{"linked_version", linked_version},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg enet_host_funcs [] = {
|
||||
{"service", host_service},
|
||||
{"check_events", host_check_events},
|
||||
{"compress_with_range_coder", host_compress_with_range_coder},
|
||||
{"connect", host_connect},
|
||||
{"flush", host_flush},
|
||||
{"broadcast", host_broadcast},
|
||||
{"channel_limit", host_channel_limit},
|
||||
{"bandwidth_limit", host_bandwidth_limit},
|
||||
// Since ENetSocket isn't part of enet-lua, we should try to keep
|
||||
// naming conventions the same as the rest of the lib.
|
||||
{"get_socket_address", host_get_socket_address},
|
||||
// We need this function to free up our ports when needed!
|
||||
{"destroy", host_gc},
|
||||
|
||||
// additional convenience functions (mostly accessors)
|
||||
{"total_sent_data", host_total_sent_data},
|
||||
{"total_received_data", host_total_received_data},
|
||||
{"service_time", host_service_time},
|
||||
{"peer_count", host_peer_count},
|
||||
{"get_peer", host_get_peer},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg enet_peer_funcs [] = {
|
||||
{"disconnect", peer_disconnect},
|
||||
{"disconnect_now", peer_disconnect_now},
|
||||
{"disconnect_later", peer_disconnect_later},
|
||||
{"reset", peer_reset},
|
||||
{"ping", peer_ping},
|
||||
{"receive", peer_receive},
|
||||
{"send", peer_send},
|
||||
{"throttle_configure", peer_throttle_configure},
|
||||
{"ping_interval", peer_ping_interval},
|
||||
{"timeout", peer_timeout},
|
||||
|
||||
// additional convenience functions to member variables
|
||||
{"index", peer_index},
|
||||
{"state", peer_state},
|
||||
{"connect_id", peer_connect_id},
|
||||
{"round_trip_time", peer_round_trip_time},
|
||||
{"last_round_trip_time", peer_last_round_trip_time},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static bool enitAlreadyInit = false;
|
||||
|
||||
int luaopen_enet(lua_State *l) {
|
||||
enet_initialize();
|
||||
if (!enitAlreadyInit) {
|
||||
atexit(enet_deinitialize);
|
||||
enitAlreadyInit = true;
|
||||
}
|
||||
|
||||
// create metatables
|
||||
luaL_newmetatable(l, "enet_host");
|
||||
lua_newtable(l); // index
|
||||
luaL_register(l, NULL, enet_host_funcs);
|
||||
lua_setfield(l, -2, "__index");
|
||||
lua_pushcfunction(l, host_gc);
|
||||
lua_setfield(l, -2, "__gc");
|
||||
|
||||
luaL_newmetatable(l, "enet_peer");
|
||||
lua_newtable(l);
|
||||
luaL_register(l, NULL, enet_peer_funcs);
|
||||
lua_setfield(l, -2, "__index");
|
||||
lua_pushcfunction(l, peer_tostring);
|
||||
lua_setfield(l, -2, "__tostring");
|
||||
|
||||
// set up peer table
|
||||
lua_newtable(l);
|
||||
|
||||
lua_newtable(l); // metatable
|
||||
lua_pushstring(l, "v");
|
||||
lua_setfield(l, -2, "__mode");
|
||||
lua_setmetatable(l, -2);
|
||||
|
||||
lua_setfield(l, LUA_REGISTRYINDEX, "enet_peers");
|
||||
|
||||
luaL_register(l, "enet", enet_funcs);
|
||||
return 1;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#include "api/api.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
int luaopen_enet(lua_State *l);
|
|
@ -59,7 +59,7 @@ static struct {
|
|||
size_t savePathLength;
|
||||
char savePath[1024];
|
||||
char source[1024];
|
||||
char requirePath[2][1024];
|
||||
char requirePath[1024];
|
||||
char identity[64];
|
||||
bool fused;
|
||||
} state;
|
||||
|
@ -116,7 +116,6 @@ bool lovrFilesystemInit(const char* argExe, const char* argGame, const char* arg
|
|||
arr_reserve(&state.archives, 2);
|
||||
|
||||
lovrFilesystemSetRequirePath("?.lua;?/init.lua");
|
||||
lovrFilesystemSetCRequirePath("??");
|
||||
|
||||
// First, try to mount a bundled archive
|
||||
const char* root = NULL;
|
||||
|
@ -407,19 +406,11 @@ size_t lovrFilesystemGetWorkingDirectory(char* buffer, size_t size) {
|
|||
}
|
||||
|
||||
const char* lovrFilesystemGetRequirePath() {
|
||||
return state.requirePath[0];
|
||||
}
|
||||
|
||||
const char* lovrFilesystemGetCRequirePath() {
|
||||
return state.requirePath[1];
|
||||
return state.requirePath;
|
||||
}
|
||||
|
||||
void lovrFilesystemSetRequirePath(const char* requirePath) {
|
||||
strncpy(state.requirePath[0], requirePath, sizeof(state.requirePath[0]) - 1);
|
||||
}
|
||||
|
||||
void lovrFilesystemSetCRequirePath(const char* requirePath) {
|
||||
strncpy(state.requirePath[1], requirePath, sizeof(state.requirePath[1]) - 1);
|
||||
strncpy(state.requirePath, requirePath, sizeof(state.requirePath) - 1);
|
||||
}
|
||||
|
||||
// Archive: dir
|
||||
|
|
|
@ -36,6 +36,4 @@ size_t lovrFilesystemGetExecutablePath(char* buffer, size_t size);
|
|||
size_t lovrFilesystemGetUserDirectory(char* buffer, size_t size);
|
||||
size_t lovrFilesystemGetWorkingDirectory(char* buffer, size_t size);
|
||||
const char* lovrFilesystemGetRequirePath(void);
|
||||
const char* lovrFilesystemGetCRequirePath(void);
|
||||
void lovrFilesystemSetRequirePath(const char* requirePath);
|
||||
void lovrFilesystemSetCRequirePath(const char* requirePath);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<uses-permission android:name="oculus.permission.HAND_TRACKING"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:allowBackup="false" android:label="LÖVR">
|
||||
<application android:allowBackup="false" android:label="LÖVR" android:extractNativeLibs="false">
|
||||
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
|
||||
<activity android:name="Activity">
|
||||
<meta-data android:name="android.app.lib_name" android:value="lovr"/>
|
||||
|
|
Loading…
Reference in New Issue