mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-22 09:01:57 -05: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
7 changed files with 141 additions and 88 deletions
|
@ -24,7 +24,16 @@ Exec=$GAME_DIR/ClassiCube
|
|||
Icon=$GAME_DIR/CCicon.png
|
||||
Path=$GAME_DIR
|
||||
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
|
||||
chmod +x $DESKTOP_FILE
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "LBackend.h"
|
||||
#include "Http.h"
|
||||
#include "Game.h"
|
||||
#include "main.h"
|
||||
|
||||
#define LAYOUTS static const struct LLayout
|
||||
#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;
|
||||
}
|
||||
|
||||
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 const cc_string defMppass = String_FromConst("(none)");
|
||||
const cc_string* user = &DirectConnectScreen.iptUsername.text;
|
||||
|
@ -492,7 +498,8 @@ static void DirectConnectScreen_StartClient(void* w) {
|
|||
if (!user->length) {
|
||||
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;
|
||||
}
|
||||
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_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_string str; char strBuffer[STRING_SIZE];
|
||||
struct MainScreen* s = &MainScreen;
|
||||
|
@ -789,9 +768,8 @@ static void MainScreen_Register(void* w) {
|
|||
|
||||
static void MainScreen_Resume(void* w) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -809,7 +787,7 @@ static void MainScreen_ResumeHover(void* w) {
|
|||
struct ResumeInfo info;
|
||||
if (s->signingIn) return;
|
||||
|
||||
MainScreen_GetResume(&info, false);
|
||||
Resume_Parse(&info, false);
|
||||
if (!info.user.length) return;
|
||||
String_InitArray(str, strBuffer);
|
||||
|
||||
|
|
|
@ -440,6 +440,7 @@ static void InitNetworking(void) {
|
|||
{
|
||||
int status = Wifi_AssocStatus();
|
||||
if (status == ASSOCSTATUS_ASSOCIATED) return;
|
||||
Platform_Log1("STATUS: %i", &status);
|
||||
|
||||
if (status == ASSOCSTATUS_CANNOTCONNECT) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------------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. */
|
||||
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
|
||||
#endif
|
||||
|
|
116
src/main.c
116
src/main.c
|
@ -9,7 +9,63 @@
|
|||
#include "Launcher.h"
|
||||
#include "Server.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) {
|
||||
cc_string title; char titleBuffer[STRING_SIZE];
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
cc_string args[GAME_MAX_CMDARGS];
|
||||
int argsCount = Platform_GetCommandLineArgs(argc, argv, args);
|
||||
struct ResumeInfo r;
|
||||
cc_string host;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* NOTE: Make sure to comment this out before pushing a commit */
|
||||
|
@ -95,41 +165,47 @@ static int RunProgram(int argc, char** argv) {
|
|||
RunGame();
|
||||
#else
|
||||
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] == ':') {
|
||||
args[0] = String_UNSAFE_SubstringAt(&args[0], 1);
|
||||
String_Copy(&Launcher_AutoHash, &args[0]);
|
||||
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])) {
|
||||
Options_Get(LOPT_USERNAME, &Game_Username, DEFAULT_USERNAME);
|
||||
String_Copy(&SP_AutoloadMap, &args[0]); /* TODO: don't copy args? */
|
||||
RunGame();
|
||||
#endif
|
||||
} else if (argsCount == 1 && DirectUrl_Claims(&args[0], &args[1], &args[2], &args[3])) {
|
||||
String_Copy(&Game_Username, &args[2]);
|
||||
String_Copy(&Game_Mppass, &args[3]);
|
||||
|
||||
if (!DirectUrl_ExtractAddress(&args[1], &Server.Address, &args[4], &Server.Port)) {
|
||||
WarnInvalidArg("Invalid port", &args[4]);
|
||||
return 1;
|
||||
}
|
||||
RunGame();
|
||||
/* mc://[addr]:[port]/[user]/[mppass] - run multiplayer using direct URL form arguments */
|
||||
} else if (argsCount == 1 && DirectUrl_Claims(&args[0], &host, &r.user, &r.mppass)) {
|
||||
DirectUrl_ExtractAddress(&host, &r.ip, &r.port);
|
||||
|
||||
if (!ParseMPArgs(&r.user, &r.mppass, &r.ip, &r.port)) return 1;
|
||||
RunGame();
|
||||
/* [user] - run multiplayer using explicit username */
|
||||
} else if (argsCount == 1) {
|
||||
String_Copy(&Game_Username, &args[0]);
|
||||
RunGame();
|
||||
RunGame();
|
||||
/* 2 to 3 arguments - unsupported at present */
|
||||
} else if (argsCount < 4) {
|
||||
WarnMissingArgs(argsCount, args);
|
||||
return 1;
|
||||
/* [user] [mppass] [address] [port] - run multiplayer using explicit arguments */
|
||||
} else {
|
||||
String_Copy(&Game_Username, &args[0]);
|
||||
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;
|
||||
}
|
||||
if (!ParseMPArgs(&args[0], &args[1], &args[2], &args[3])) return 1;
|
||||
RunGame();
|
||||
}
|
||||
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…
Reference in a new issue