mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-13 17:47:12 -04:00
Add --singleplayer and --resume support to ClassiCube command lline, also add support it in the desktop entry file
This commit is contained in:
parent
c603018d48
commit
4a13eec6c8
@ -24,7 +24,16 @@ Exec=$GAME_DIR/ClassiCube
|
|||||||
Icon=$GAME_DIR/CCicon.png
|
Icon=$GAME_DIR/CCicon.png
|
||||||
Path=$GAME_DIR
|
Path=$GAME_DIR
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Game
|
Categories=Game;
|
||||||
|
Actions=singleplayer;resume;
|
||||||
|
|
||||||
|
[Desktop Action singleplayer]
|
||||||
|
Name=Start singleplayer
|
||||||
|
Exec=$GAME_DIR/ClassiCube --singleplayer
|
||||||
|
|
||||||
|
[Desktop Action resume]
|
||||||
|
Name=Resume last server
|
||||||
|
Exec=$GAME_DIR/ClassiCube --resume
|
||||||
EOF
|
EOF
|
||||||
chmod +x $DESKTOP_FILE
|
chmod +x $DESKTOP_FILE
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "LBackend.h"
|
#include "LBackend.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
#define LAYOUTS static const struct LLayout
|
#define LAYOUTS static const struct LLayout
|
||||||
#define IsBackButton(btn) (btn == CCKEY_ESCAPE || btn == CCPAD_SELECT || btn == CCPAD_2)
|
#define IsBackButton(btn) (btn == CCKEY_ESCAPE || btn == CCPAD_SELECT || btn == CCPAD_2)
|
||||||
@ -473,6 +474,11 @@ static void DirectConnectScreen_UrlFilter(cc_string* str) {
|
|||||||
str->length = 0;
|
str->length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static cc_bool DirectConnectScreen_ParsePort(const cc_string* str) {
|
||||||
|
int port;
|
||||||
|
return Convert_ParseInt(str, &port) && port >= 0 && port <= 65535;
|
||||||
|
}
|
||||||
|
|
||||||
static void DirectConnectScreen_StartClient(void* w) {
|
static void DirectConnectScreen_StartClient(void* w) {
|
||||||
static const cc_string defMppass = String_FromConst("(none)");
|
static const cc_string defMppass = String_FromConst("(none)");
|
||||||
const cc_string* user = &DirectConnectScreen.iptUsername.text;
|
const cc_string* user = &DirectConnectScreen.iptUsername.text;
|
||||||
@ -492,7 +498,8 @@ static void DirectConnectScreen_StartClient(void* w) {
|
|||||||
if (!user->length) {
|
if (!user->length) {
|
||||||
LLabel_SetConst(status, "&cUsername required"); return;
|
LLabel_SetConst(status, "&cUsername required"); return;
|
||||||
}
|
}
|
||||||
if (!DirectUrl_ExtractAddress(addr, &ip, &port, &raw_port)) {
|
DirectUrl_ExtractAddress(addr, &ip, &port);
|
||||||
|
if (!DirectConnectScreen_ParsePort(&port)) {
|
||||||
LLabel_SetConst(status, "&cInvalid port"); return;
|
LLabel_SetConst(status, "&cInvalid port"); return;
|
||||||
}
|
}
|
||||||
if (Socket_ParseAddress(&ip, 25565, addrs, &numAddrs)) {
|
if (Socket_ParseAddress(&ip, 25565, addrs, &numAddrs)) {
|
||||||
@ -721,34 +728,6 @@ LAYOUTS main_btnUpdates[] = { { ANCHOR_MAX, 6 }, { ANCHOR_MAX, 6 } };
|
|||||||
LAYOUTS main_lblUpdate_N[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 45 } };
|
LAYOUTS main_lblUpdate_N[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 45 } };
|
||||||
LAYOUTS main_lblUpdate_H[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 6 } };
|
LAYOUTS main_lblUpdate_H[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 6 } };
|
||||||
|
|
||||||
|
|
||||||
struct ResumeInfo {
|
|
||||||
cc_string user, ip, port, server, mppass;
|
|
||||||
char _userBuffer[STRING_SIZE], _serverBuffer[STRING_SIZE];
|
|
||||||
char _ipBuffer[16], _portBuffer[16], _mppassBuffer[STRING_SIZE];
|
|
||||||
cc_bool valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
CC_NOINLINE static void MainScreen_GetResume(struct ResumeInfo* info, cc_bool full) {
|
|
||||||
String_InitArray(info->server, info->_serverBuffer);
|
|
||||||
Options_Get(ROPT_SERVER, &info->server, "");
|
|
||||||
String_InitArray(info->user, info->_userBuffer);
|
|
||||||
Options_Get(ROPT_USER, &info->user, "");
|
|
||||||
|
|
||||||
String_InitArray(info->ip, info->_ipBuffer);
|
|
||||||
Options_Get(ROPT_IP, &info->ip, "");
|
|
||||||
String_InitArray(info->port, info->_portBuffer);
|
|
||||||
Options_Get(ROPT_PORT, &info->port, "");
|
|
||||||
|
|
||||||
if (!full) return;
|
|
||||||
String_InitArray(info->mppass, info->_mppassBuffer);
|
|
||||||
Options_GetSecure(ROPT_MPPASS, &info->mppass);
|
|
||||||
|
|
||||||
info->valid =
|
|
||||||
info->user.length && info->mppass.length &&
|
|
||||||
info->ip.length && info->port.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
CC_NOINLINE static void MainScreen_Error(struct HttpRequest* req, const char* action) {
|
CC_NOINLINE static void MainScreen_Error(struct HttpRequest* req, const char* action) {
|
||||||
cc_string str; char strBuffer[STRING_SIZE];
|
cc_string str; char strBuffer[STRING_SIZE];
|
||||||
struct MainScreen* s = &MainScreen;
|
struct MainScreen* s = &MainScreen;
|
||||||
@ -789,9 +768,8 @@ static void MainScreen_Register(void* w) {
|
|||||||
|
|
||||||
static void MainScreen_Resume(void* w) {
|
static void MainScreen_Resume(void* w) {
|
||||||
struct ResumeInfo info;
|
struct ResumeInfo info;
|
||||||
MainScreen_GetResume(&info, true);
|
|
||||||
|
|
||||||
if (!info.valid) return;
|
if (!Resume_Parse(&info, true)) return;
|
||||||
Launcher_StartGame(&info.user, &info.mppass, &info.ip, &info.port, &info.server, 1);
|
Launcher_StartGame(&info.user, &info.mppass, &info.ip, &info.port, &info.server, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,7 +787,7 @@ static void MainScreen_ResumeHover(void* w) {
|
|||||||
struct ResumeInfo info;
|
struct ResumeInfo info;
|
||||||
if (s->signingIn) return;
|
if (s->signingIn) return;
|
||||||
|
|
||||||
MainScreen_GetResume(&info, false);
|
Resume_Parse(&info, false);
|
||||||
if (!info.user.length) return;
|
if (!info.user.length) return;
|
||||||
String_InitArray(str, strBuffer);
|
String_InitArray(str, strBuffer);
|
||||||
|
|
||||||
|
@ -440,6 +440,7 @@ static void InitNetworking(void) {
|
|||||||
{
|
{
|
||||||
int status = Wifi_AssocStatus();
|
int status = Wifi_AssocStatus();
|
||||||
if (status == ASSOCSTATUS_ASSOCIATED) return;
|
if (status == ASSOCSTATUS_ASSOCIATED) return;
|
||||||
|
Platform_Log1("STATUS: %i", &status);
|
||||||
|
|
||||||
if (status == ASSOCSTATUS_CANNOTCONNECT) {
|
if (status == ASSOCSTATUS_CANNOTCONNECT) {
|
||||||
Platform_LogConst("Can't connect to WIFI");
|
Platform_LogConst("Can't connect to WIFI");
|
||||||
|
32
src/Utils.c
32
src/Utils.c
@ -366,35 +366,3 @@ int EntryList_Find(struct StringsBuffer* list, const cc_string* key, char separa
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*########################################################################################################################*
|
|
||||||
*--------------------------------------------------------Direct URL-------------------------------------------------------*
|
|
||||||
*#########################################################################################################################*/
|
|
||||||
cc_bool DirectUrl_Claims(const cc_string* input, cc_string* addr, cc_string* user, cc_string* mppass) {
|
|
||||||
static const cc_string prefix = String_FromConst("mc://");
|
|
||||||
cc_string parts[6];
|
|
||||||
if (!String_CaselessStarts(input, &prefix)) return false;
|
|
||||||
|
|
||||||
/* mc://[ip:port]/[username]/[mppass] */
|
|
||||||
if (String_UNSAFE_Split(input, '/', parts, 6) != 5) return false;
|
|
||||||
|
|
||||||
*addr = parts[2];
|
|
||||||
*user = parts[3];
|
|
||||||
*mppass = parts[4];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_bool DirectUrl_ExtractAddress(const cc_string* addr, cc_string* ip, cc_string* port, int* portNum) {
|
|
||||||
static const cc_string defPort = String_FromConst("25565");
|
|
||||||
int index = String_LastIndexOf(addr, ':');
|
|
||||||
|
|
||||||
/* support either "[IP]" or "[IP]:[PORT]" */
|
|
||||||
if (index == -1) {
|
|
||||||
*ip = *addr;
|
|
||||||
*port = defPort;
|
|
||||||
} else {
|
|
||||||
*ip = String_UNSAFE_Substring(addr, 0, index);
|
|
||||||
*port = String_UNSAFE_SubstringAt(addr, index + 1);
|
|
||||||
}
|
|
||||||
return Convert_ParseInt(port, portNum) && *portNum >= 0 && *portNum <= 65535;
|
|
||||||
}
|
|
||||||
|
@ -77,8 +77,5 @@ CC_NOINLINE STRING_REF cc_string EntryList_UNSAFE_Get(struct StringsBuffer* list
|
|||||||
/* Finds the index of the entry whose key caselessly equals the given key. */
|
/* Finds the index of the entry whose key caselessly equals the given key. */
|
||||||
CC_NOINLINE int EntryList_Find(struct StringsBuffer* list, const cc_string* key, char separator);
|
CC_NOINLINE int EntryList_Find(struct StringsBuffer* list, const cc_string* key, char separator);
|
||||||
|
|
||||||
cc_bool DirectUrl_Claims(const cc_string* STRING_REF input, cc_string* addr, cc_string* user, cc_string* mppass);
|
|
||||||
cc_bool DirectUrl_ExtractAddress(const cc_string* STRING_REF addr, cc_string* ip, cc_string* port, int* portNum);
|
|
||||||
|
|
||||||
CC_END_HEADER
|
CC_END_HEADER
|
||||||
#endif
|
#endif
|
||||||
|
110
src/main.c
110
src/main.c
@ -9,7 +9,63 @@
|
|||||||
#include "Launcher.h"
|
#include "Launcher.h"
|
||||||
#include "Server.h"
|
#include "Server.h"
|
||||||
#include "Options.h"
|
#include "Options.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
/*########################################################################################################################*
|
||||||
|
*-------------------------------------------------Complex argument parsing------------------------------------------------*
|
||||||
|
*#########################################################################################################################*/
|
||||||
|
cc_bool Resume_Parse(struct ResumeInfo* info, cc_bool full) {
|
||||||
|
String_InitArray(info->server, info->_serverBuffer);
|
||||||
|
Options_Get(ROPT_SERVER, &info->server, "");
|
||||||
|
String_InitArray(info->user, info->_userBuffer);
|
||||||
|
Options_Get(ROPT_USER, &info->user, "");
|
||||||
|
|
||||||
|
String_InitArray(info->ip, info->_ipBuffer);
|
||||||
|
Options_Get(ROPT_IP, &info->ip, "");
|
||||||
|
String_InitArray(info->port, info->_portBuffer);
|
||||||
|
Options_Get(ROPT_PORT, &info->port, "");
|
||||||
|
|
||||||
|
if (!full) return true;
|
||||||
|
String_InitArray(info->mppass, info->_mppassBuffer);
|
||||||
|
Options_GetSecure(ROPT_MPPASS, &info->mppass);
|
||||||
|
|
||||||
|
return
|
||||||
|
info->user.length && info->mppass.length &&
|
||||||
|
info->ip.length && info->port.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_bool DirectUrl_Claims(const cc_string* input, cc_string* addr, cc_string* user, cc_string* mppass) {
|
||||||
|
static const cc_string prefix = String_FromConst("mc://");
|
||||||
|
cc_string parts[6];
|
||||||
|
if (!String_CaselessStarts(input, &prefix)) return false;
|
||||||
|
|
||||||
|
/* mc://[ip:port]/[username]/[mppass] */
|
||||||
|
if (String_UNSAFE_Split(input, '/', parts, 6) != 5) return false;
|
||||||
|
|
||||||
|
*addr = parts[2];
|
||||||
|
*user = parts[3];
|
||||||
|
*mppass = parts[4];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectUrl_ExtractAddress(const cc_string* addr, cc_string* ip, cc_string* port) {
|
||||||
|
static const cc_string defPort = String_FromConst("25565");
|
||||||
|
int index = String_LastIndexOf(addr, ':');
|
||||||
|
|
||||||
|
/* support either "[IP]" or "[IP]:[PORT]" */
|
||||||
|
if (index == -1) {
|
||||||
|
*ip = *addr;
|
||||||
|
*port = defPort;
|
||||||
|
} else {
|
||||||
|
*ip = String_UNSAFE_Substring(addr, 0, index);
|
||||||
|
*port = String_UNSAFE_SubstringAt(addr, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*########################################################################################################################*
|
||||||
|
*------------------------------------------------------Game setup/run-----------------------------------------------------*
|
||||||
|
*#########################################################################################################################*/
|
||||||
static void RunGame(void) {
|
static void RunGame(void) {
|
||||||
cc_string title; char titleBuffer[STRING_SIZE];
|
cc_string title; char titleBuffer[STRING_SIZE];
|
||||||
int width = Options_GetInt(OPT_WINDOW_WIDTH, 0, DisplayInfo.Width, 0);
|
int width = Options_GetInt(OPT_WINDOW_WIDTH, 0, DisplayInfo.Width, 0);
|
||||||
@ -78,9 +134,23 @@ static cc_bool IsOpenableFile(const cc_string* path) {
|
|||||||
return File_Exists(&str);
|
return File_Exists(&str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ParseMPArgs(const cc_string* user, const cc_string* mppass, const cc_string* addr, const cc_string* port) {
|
||||||
|
String_Copy(&Game_Username, user);
|
||||||
|
String_Copy(&Game_Mppass, mppass);
|
||||||
|
String_Copy(&Server.Address, addr);
|
||||||
|
|
||||||
|
if (!Convert_ParseInt(port, &Server.Port) || Server.Port < 0 || Server.Port > 65535) {
|
||||||
|
WarnInvalidArg("Invalid port", port);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int RunProgram(int argc, char** argv) {
|
static int RunProgram(int argc, char** argv) {
|
||||||
cc_string args[GAME_MAX_CMDARGS];
|
cc_string args[GAME_MAX_CMDARGS];
|
||||||
int argsCount = Platform_GetCommandLineArgs(argc, argv, args);
|
int argsCount = Platform_GetCommandLineArgs(argc, argv, args);
|
||||||
|
struct ResumeInfo r;
|
||||||
|
cc_string host;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
/* NOTE: Make sure to comment this out before pushing a commit */
|
/* NOTE: Make sure to comment this out before pushing a commit */
|
||||||
@ -95,41 +165,47 @@ static int RunProgram(int argc, char** argv) {
|
|||||||
RunGame();
|
RunGame();
|
||||||
#else
|
#else
|
||||||
Launcher_Run();
|
Launcher_Run();
|
||||||
/* :hash to auto join server with the given hash */
|
/* :[hash] - auto join server with the given hash */
|
||||||
} else if (argsCount == 1 && args[0].buffer[0] == ':') {
|
} else if (argsCount == 1 && args[0].buffer[0] == ':') {
|
||||||
args[0] = String_UNSAFE_SubstringAt(&args[0], 1);
|
args[0] = String_UNSAFE_SubstringAt(&args[0], 1);
|
||||||
String_Copy(&Launcher_AutoHash, &args[0]);
|
String_Copy(&Launcher_AutoHash, &args[0]);
|
||||||
Launcher_Run();
|
Launcher_Run();
|
||||||
/* File path to auto load a map in singleplayer */
|
/* --resume - try to resume to last server */
|
||||||
|
} else if (argsCount == 1 && String_CaselessEqualsConst(&args[0], DEFAULT_RESUME_ARG)) {
|
||||||
|
if (!Resume_Parse(&r, true)) {
|
||||||
|
WarnInvalidArg("No server to resume to", &args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ParseMPArgs(&r.user, &r.mppass, &r.ip, &r.port)) return 1;
|
||||||
|
RunGame();
|
||||||
|
/* --singleplayer' - run singleplayer with default user */
|
||||||
|
} else if (argsCount == 1 && String_CaselessEqualsConst(&args[0], DEFAULT_SINGLEPLAYER_ARG)) {
|
||||||
|
Options_Get(LOPT_USERNAME, &Game_Username, DEFAULT_USERNAME);
|
||||||
|
RunGame();
|
||||||
|
/* [file path] - run singleplayer with auto loaded map */
|
||||||
} else if (argsCount == 1 && IsOpenableFile(&args[0])) {
|
} else if (argsCount == 1 && IsOpenableFile(&args[0])) {
|
||||||
Options_Get(LOPT_USERNAME, &Game_Username, DEFAULT_USERNAME);
|
Options_Get(LOPT_USERNAME, &Game_Username, DEFAULT_USERNAME);
|
||||||
String_Copy(&SP_AutoloadMap, &args[0]); /* TODO: don't copy args? */
|
String_Copy(&SP_AutoloadMap, &args[0]); /* TODO: don't copy args? */
|
||||||
RunGame();
|
RunGame();
|
||||||
#endif
|
#endif
|
||||||
} else if (argsCount == 1 && DirectUrl_Claims(&args[0], &args[1], &args[2], &args[3])) {
|
/* mc://[addr]:[port]/[user]/[mppass] - run multiplayer using direct URL form arguments */
|
||||||
String_Copy(&Game_Username, &args[2]);
|
} else if (argsCount == 1 && DirectUrl_Claims(&args[0], &host, &r.user, &r.mppass)) {
|
||||||
String_Copy(&Game_Mppass, &args[3]);
|
DirectUrl_ExtractAddress(&host, &r.ip, &r.port);
|
||||||
|
|
||||||
if (!DirectUrl_ExtractAddress(&args[1], &Server.Address, &args[4], &Server.Port)) {
|
if (!ParseMPArgs(&r.user, &r.mppass, &r.ip, &r.port)) return 1;
|
||||||
WarnInvalidArg("Invalid port", &args[4]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
RunGame();
|
RunGame();
|
||||||
|
/* [user] - run multiplayer using explicit username */
|
||||||
} else if (argsCount == 1) {
|
} else if (argsCount == 1) {
|
||||||
String_Copy(&Game_Username, &args[0]);
|
String_Copy(&Game_Username, &args[0]);
|
||||||
RunGame();
|
RunGame();
|
||||||
|
/* 2 to 3 arguments - unsupported at present */
|
||||||
} else if (argsCount < 4) {
|
} else if (argsCount < 4) {
|
||||||
WarnMissingArgs(argsCount, args);
|
WarnMissingArgs(argsCount, args);
|
||||||
return 1;
|
return 1;
|
||||||
|
/* [user] [mppass] [address] [port] - run multiplayer using explicit arguments */
|
||||||
} else {
|
} else {
|
||||||
String_Copy(&Game_Username, &args[0]);
|
if (!ParseMPArgs(&args[0], &args[1], &args[2], &args[3])) return 1;
|
||||||
String_Copy(&Game_Mppass, &args[1]);
|
|
||||||
String_Copy(&Server.Address,&args[2]);
|
|
||||||
|
|
||||||
if (!Convert_ParseInt(&args[3], &Server.Port) || Server.Port < 0 || Server.Port > 65535) {
|
|
||||||
WarnInvalidArg("Invalid port", &args[3]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
RunGame();
|
RunGame();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
24
src/main.h
Normal file
24
src/main.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef CC_MAIN_H
|
||||||
|
#define CC_MAIN_H
|
||||||
|
#include "String.h"
|
||||||
|
/* Utility constants and methods for command line arguments
|
||||||
|
Copyright 2014-2023 ClassiCube | Licensed under BSD-3
|
||||||
|
*/
|
||||||
|
CC_BEGIN_HEADER
|
||||||
|
|
||||||
|
#define DEFAULT_SINGLEPLAYER_ARG "--singleplayer"
|
||||||
|
#define DEFAULT_RESUME_ARG "--resume"
|
||||||
|
|
||||||
|
struct ResumeInfo {
|
||||||
|
cc_string user, ip, port, server, mppass;
|
||||||
|
char _userBuffer[STRING_SIZE], _serverBuffer[STRING_SIZE];
|
||||||
|
char _ipBuffer[16], _portBuffer[16], _mppassBuffer[STRING_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
cc_bool Resume_Parse(struct ResumeInfo* info, cc_bool full);
|
||||||
|
|
||||||
|
cc_bool DirectUrl_Claims(const cc_string* STRING_REF input, cc_string* addr, cc_string* user, cc_string* mppass);
|
||||||
|
void DirectUrl_ExtractAddress(const cc_string* STRING_REF addr, cc_string* ip, cc_string* port);
|
||||||
|
|
||||||
|
CC_END_HEADER
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user