From 2c2ae11a123c5faad5807508e32fcb33c8d2bde7 Mon Sep 17 00:00:00 2001 From: terencehill Date: Thu, 17 Nov 2016 13:46:59 +0100 Subject: [PATCH] Show a welcome window with banner, IP and MOTD on server connection (WIP: need a way to retrieve own server's IP; usual welcome message not removed yet) --- commands.cfg | 1 + gfx/menu/luma/no_banner.tga | Bin 0 -> 49 bytes gfx/menu/luma/skinvalues.txt | 3 +- gfx/menu/luminos/skinvalues.txt | 2 + gfx/menu/wickedx/skinvalues.txt | 2 + gfx/menu/xaw/skinvalues.txt | 2 + qcsrc/client/main.qc | 36 ++++++++++ qcsrc/common/net_linked.qh | 2 + qcsrc/common/util.qc | 10 +++ qcsrc/common/util.qh | 1 + qcsrc/menu/command/menu_cmd.qc | 22 ++++++ qcsrc/menu/skin-customizables.inc | 1 + qcsrc/menu/xonotic/_mod.inc | 2 + qcsrc/menu/xonotic/_mod.qh | 1 + qcsrc/menu/xonotic/dialog_welcome.qc | 101 +++++++++++++++++++++++++++ qcsrc/menu/xonotic/dialog_welcome.qh | 23 ++++++ qcsrc/menu/xonotic/mainwindow.qc | 5 ++ qcsrc/menu/xonotic/textbox.qc | 67 ++++++++++++++++++ qcsrc/menu/xonotic/textbox.qh | 30 ++++++++ qcsrc/server/client.qc | 32 +++++++++ qcsrc/server/command/cmd.qc | 22 ++++++ qcsrc/server/command/cmd.qh | 3 + 22 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 gfx/menu/luma/no_banner.tga create mode 100644 qcsrc/menu/xonotic/dialog_welcome.qc create mode 100644 qcsrc/menu/xonotic/dialog_welcome.qh create mode 100644 qcsrc/menu/xonotic/textbox.qc create mode 100644 qcsrc/menu/xonotic/textbox.qh diff --git a/commands.cfg b/commands.cfg index a8de9b95c..8b8c14ba7 100644 --- a/commands.cfg +++ b/commands.cfg @@ -147,6 +147,7 @@ seta cl_autoswitch 1 "automatically switch to newly picked up weapons if they ar // commented out commands are really only intended for internal use, or already have declaration in the engine alias autoswitch "qc_cmd_cmd autoswitch ${* ?}" // Whether or not to switch automatically when getting a better weapon alias clientversion "qc_cmd_cmd clientversion ${* ?}" // Release version of the game +//alias getserverpic "qc_cmd_cmd getserverpic ${* ?}" // Retrieve server banner from the server //alias mv_getpicture "qc_cmd_cmd mv_getpicture ${* ?}" // Retrieve mapshot picture from the server alias join "qc_cmd_cmd join ${* ?}" // Become a player in the game alias ready "qc_cmd_cmd ready ${* ?}" // Qualify as ready to end warmup stage (or restart server if allowed) diff --git a/gfx/menu/luma/no_banner.tga b/gfx/menu/luma/no_banner.tga new file mode 100644 index 0000000000000000000000000000000000000000..be731d043c6d5aba6d88072d0a04743136c29001 GIT binary patch literal 49 ocmZQz;9`IQMg~R(1r8VwOos%8x`ufMd;0t7M!0$Uxau(g06L@vAOHXW literal 0 HcmV?d00001 diff --git a/gfx/menu/luma/skinvalues.txt b/gfx/menu/luma/skinvalues.txt index c997fea25..7531c7eea 100644 --- a/gfx/menu/luma/skinvalues.txt +++ b/gfx/menu/luma/skinvalues.txt @@ -92,9 +92,10 @@ COLOR_DIALOG_VIEW '1 1 1' COLOR_DIALOG_MODEL '1 1 1' COLOR_DIALOG_CROSSHAIR '1 1 1' COLOR_DIALOG_HUD '1 1 1' -COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1' COLOR_DIALOG_SERVERINFO '1 1 1' +COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1' COLOR_DIALOG_FIRSTRUN '1 1 1' +COLOR_DIALOG_WELCOME '1 1 1' COLOR_DIALOG_CVARS '1 0.2 0.15' COLOR_DIALOG_HUDCONFIRM '1 0.2 0.15' diff --git a/gfx/menu/luminos/skinvalues.txt b/gfx/menu/luminos/skinvalues.txt index ca0384fb5..6eadef16a 100755 --- a/gfx/menu/luminos/skinvalues.txt +++ b/gfx/menu/luminos/skinvalues.txt @@ -193,6 +193,8 @@ COLOR_DIALOG_CROSSHAIR '1 1 1' COLOR_DIALOG_HUD '1 1 1' COLOR_DIALOG_SERVERINFO '1 1 1' COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1' +COLOR_DIALOG_FIRSTRUN '1 1 1' +COLOR_DIALOG_WELCOME '1 1 1' COLOR_DIALOG_CVARS '1 0 0' COLOR_DIALOG_HUDCONFIRM '1 0 0' diff --git a/gfx/menu/wickedx/skinvalues.txt b/gfx/menu/wickedx/skinvalues.txt index b7011a0b0..d4fbad9f8 100644 --- a/gfx/menu/wickedx/skinvalues.txt +++ b/gfx/menu/wickedx/skinvalues.txt @@ -193,6 +193,8 @@ COLOR_DIALOG_CROSSHAIR '1 1 1' COLOR_DIALOG_HUD '1 1 1' COLOR_DIALOG_SERVERINFO '1 1 1' COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1' +COLOR_DIALOG_FIRSTRUN '1 1 1' +COLOR_DIALOG_WELCOME '1 1 1' COLOR_DIALOG_CVARS '1 0 0' COLOR_DIALOG_HUDCONFIRM '1 0 0' diff --git a/gfx/menu/xaw/skinvalues.txt b/gfx/menu/xaw/skinvalues.txt index 5f4bbaad4..01f3c9e54 100644 --- a/gfx/menu/xaw/skinvalues.txt +++ b/gfx/menu/xaw/skinvalues.txt @@ -37,6 +37,8 @@ COLOR_DIALOG_CROSSHAIR '1 1 1' COLOR_DIALOG_HUD '1 1 1' COLOR_DIALOG_SERVERINFO '1 1 1' COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1' +COLOR_DIALOG_FIRSTRUN '1 1 1' +COLOR_DIALOG_WELCOME '1 1 1' COLOR_DIALOG_CVARS '1 0 0' COLOR_DIALOG_HUDCONFIRM '1 0 0' diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 36ca21724..024029019 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -1226,6 +1226,42 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) } } +string welcomedialog_args; +NET_HANDLE(TE_CSQC_SERVERINFO, bool isNew) +{ + if(welcomedialog_args) + strunzone(welcomedialog_args); + welcomedialog_args = strcat("name \"", ReadString(), "\""); + welcomedialog_args = strcat(welcomedialog_args, " ip \"", ReadString(), "\""); + welcomedialog_args = strcat(welcomedialog_args, " motd \"", MakeConsoleSafe(strreplace("\n", "\\n", ReadString())), "\""); + string pic = ReadString(); + if(pic == "" || PreviewExists(pic) ) + { + if(pic != "") + welcomedialog_args = strcat(welcomedialog_args, " pic \"", pic, "\""); + localcmd("\nmenu_cmd directmenu Welcome ", welcomedialog_args, "\n"); + welcomedialog_args = string_null; + } else { + welcomedialog_args = strzone(welcomedialog_args); + print(_("Requesting server banner...\n")); + localcmd("\ncmd getserverpic\n"); + } + return true; +} + +NET_HANDLE(TE_CSQC_SERVERINFO_PIC, bool isNew) +{ + if(welcomedialog_args) + { + localcmd("\nmenu_cmd directmenu Welcome ", strcat(welcomedialog_args, " pic \"", ReadPicture(), "\""), "\n"); + strunzone(welcomedialog_args); + welcomedialog_args = string_null; + } + else // just get the image, we don't want to display the welcome dialog + ReadPicture(); + return true; +} + string _getcommandkey(string cmd_name, string command, bool forcename) { string keys; diff --git a/qcsrc/common/net_linked.qh b/qcsrc/common/net_linked.qh index 9cd2094a6..1fcb3d12a 100644 --- a/qcsrc/common/net_linked.qh +++ b/qcsrc/common/net_linked.qh @@ -5,6 +5,8 @@ REGISTER_NET_TEMP(TE_CSQC_RACE) REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER) REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT) REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN) +REGISTER_NET_TEMP(TE_CSQC_SERVERINFO) +REGISTER_NET_TEMP(TE_CSQC_SERVERINFO_PIC) REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP) const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 87636d24e..7c7cadecf 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -37,6 +37,16 @@ vector real_origin(entity ent) return v; } #endif + +bool ImageExists(string img) +{ + if(fexists(strcat(img, ".tga"))) return true; + if(fexists(strcat(img, ".png"))) return true; + if(fexists(strcat(img, ".jpg"))) return true; + if(fexists(strcat(img, ".pcx"))) return true; + + return false; +} string wordwrap_buffer; diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 57f9a2217..8b1bd064e 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -32,6 +32,7 @@ string ftos_decimals(float number, float decimals); string ftos_mindecimals(float number); bool fexists(string f); +bool ImageExists(string img); // unzone the string, and return it as tempstring. Safe to be called on string_null string fstrunzone(string s); diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index 72aadf624..571dfdb05 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -7,6 +7,7 @@ #include +.void(entity me, float argsbuf) readInputArgs; .entity firstChild, nextSibling; string _dumptree_space; @@ -94,6 +95,27 @@ void GameCommand(string theCommand) m_play_click_sound(MENU_SOUND_OPEN); m_goto(strcat(filter, argv(1))); // switch to a menu item } + else if(argc > 2 && !isdemo()) + { + entity e = NULL; + float argsbuf = 0; + string s = strzone(argv(1)); // dialog name + for(int i = 0; (e = nextent(e)); ) + if(e.classname != "vtbl" && e.name == strcat(filter, s)) + { + argsbuf = buf_create(); + if(argsbuf >= 0) + if(e.readInputArgs) + { + for(i = 2; i < argc; ++i) + bufstr_add(argsbuf, argv(i), 1); + e.readInputArgs(e, argsbuf); + m_goto(strcat(filter, s)); + } + if(argsbuf >= 0) + buf_del(argsbuf); + } + } return; } diff --git a/qcsrc/menu/skin-customizables.inc b/qcsrc/menu/skin-customizables.inc index d5e1f82eb..d73d7e7db 100644 --- a/qcsrc/menu/skin-customizables.inc +++ b/qcsrc/menu/skin-customizables.inc @@ -70,6 +70,7 @@ SKINBEGIN SKINVECTOR(COLOR_DIALOG_CROSSHAIR, '1 0.7 0.7'); SKINVECTOR(COLOR_DIALOG_HUD, '1 0.7 0.7'); SKINVECTOR(COLOR_DIALOG_SERVERINFO, '0.7 0.7 1'); + SKINVECTOR(COLOR_DIALOG_WELCOME, '1 0.7 0.7'); SKINVECTOR(COLOR_DIALOG_CVARS, '1 0 0'); SKINVECTOR(COLOR_DIALOG_SCREENSHOTVIEWER, '0.7 0.7 1'); SKINVECTOR(COLOR_DIALOG_HUDCONFIRM, '1 0 0'); diff --git a/qcsrc/menu/xonotic/_mod.inc b/qcsrc/menu/xonotic/_mod.inc index 577c82258..18e77ba38 100644 --- a/qcsrc/menu/xonotic/_mod.inc +++ b/qcsrc/menu/xonotic/_mod.inc @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/menu/xonotic/_mod.qh b/qcsrc/menu/xonotic/_mod.qh index b6e34eff2..741d86800 100644 --- a/qcsrc/menu/xonotic/_mod.qh +++ b/qcsrc/menu/xonotic/_mod.qh @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/menu/xonotic/dialog_welcome.qc b/qcsrc/menu/xonotic/dialog_welcome.qc new file mode 100644 index 000000000..6ef42a579 --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_welcome.qc @@ -0,0 +1,101 @@ +#include "dialog_welcome.qh" + +#include "image.qh" +#include "textlabel.qh" +#include "textbox.qh" +#include "radiobutton.qh" +#include "commandbutton.qh" +#include "slider.qh" + +void welcomeDialog_resetStrings(entity me) +{ + if(me.serverinfo_name) + strunzone(me.serverinfo_name); + me.serverinfo_name = strzone(_("")); + + if(me.serverinfo_ip) + strunzone(me.serverinfo_ip); + me.serverinfo_ip = strzone(_("")); + + if(me.serverinfo_MOTD) + strunzone(me.serverinfo_MOTD); + me.serverinfo_MOTD = strzone(_("")); + + if(me.serverinfo_pic) + strunzone(me.serverinfo_pic); + me.serverinfo_pic = strzone("no_banner"); +} +void XonoticWelcomeDialog_configureDialog(entity me) +{ + welcomeDialog_resetStrings(me); + SUPER(XonoticWelcomeDialog).configureDialog(me); +} +void XonoticWelcomeDialog_readInputArgs(entity me, float argsbuf) +{ + int i = 0; + string s; + welcomeDialog_resetStrings(me); + if(argsbuf >= 0) + while((s = bufstr_get(argsbuf, i)) != "") + { + if(s == "name") + { + if(me.serverinfo_name) + strunzone(me.serverinfo_name); + me.serverinfo_name = strzone(bufstr_get(argsbuf, i + 1)); + ++i; + } + else if(s == "ip") + { + if(me.serverinfo_ip) + strunzone(me.serverinfo_ip); + me.serverinfo_ip = strzone(bufstr_get(argsbuf, i + 1)); + ++i; + } + else if(s == "motd") + { + if(me.serverinfo_MOTD) + strunzone(me.serverinfo_MOTD); + me.serverinfo_MOTD = strzone(bufstr_get(argsbuf, i + 1)); + ++i; + } + else if(s == "pic") + { + if(me.serverinfo_pic) + strunzone(me.serverinfo_pic); + me.serverinfo_pic = strzone(strcat("/", bufstr_get(argsbuf, i + 1))); + ++i; + } + ++i; + } + me.serverinfo_name_ent.setText(me.serverinfo_name_ent, me.serverinfo_name); + me.serverinfo_ip_ent.setText(me.serverinfo_ip_ent, me.serverinfo_ip); + me.serverinfo_MOTD_ent.setText(me.serverinfo_MOTD_ent, me.serverinfo_MOTD); + + if(me.serverinfo_pic_ent.src) + strunzone(me.serverinfo_pic_ent.src); + me.serverinfo_pic_ent.src = strzone(me.serverinfo_pic); +} + +void XonoticWelcomeDialog_fill(entity me) +{ + entity e; + + me.TR(me); + me.TD(me, 4, me.columns / 2, me.serverinfo_pic_ent = makeXonoticImage(string_null, 4.0/3.0)); + + me.gotoRC(me, 1, me.columns / 2); + me.TD(me, 1, me.columns / 2, me.serverinfo_name_ent = makeXonoticTextLabel(0, "")); + me.gotoRC(me, 2, me.columns / 2); + me.TD(me, 1, me.columns / 2, me.serverinfo_ip_ent = makeXonoticTextLabel(0, "")); + + me.gotoRC(me, 4, 0); + me.TD(me, me.rows - 4 - 1, me.columns, me.serverinfo_MOTD_ent = makeXonoticTextBox()); + me.serverinfo_MOTD_ent.allowColors = 1; + me.gotoRC(me, me.rows - 1, 0); + me.TD(me, 1, me.columns / 2, e = makeXonoticCommandButton(_("Disconnect"), '0 0 0', "disconnect", COMMANDBUTTON_CLOSE)); + me.TD(me, 1, me.columns / 2, e = makeXonoticButton(_("OK"), '0 0 0')); + e.onClick = Dialog_Close; + e.onClickEntity = me; + e.preferredFocusPriority = 1; +} diff --git a/qcsrc/menu/xonotic/dialog_welcome.qh b/qcsrc/menu/xonotic/dialog_welcome.qh new file mode 100644 index 000000000..e3b569f24 --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_welcome.qh @@ -0,0 +1,23 @@ +#pragma once + +#include "rootdialog.qh" +CLASS(XonoticWelcomeDialog, XonoticRootDialog) + METHOD(XonoticWelcomeDialog, fill, void(entity)); + ATTRIB(XonoticWelcomeDialog, title, string, _("Welcome")); + ATTRIB(XonoticWelcomeDialog, color, vector, SKINCOLOR_DIALOG_WELCOME); + ATTRIB(XonoticWelcomeDialog, intendedWidth, float, 0.7); + ATTRIB(XonoticWelcomeDialog, rows, float, 14); + ATTRIB(XonoticWelcomeDialog, columns, float, 4); + ATTRIB(XonoticWelcomeDialog, name, string, "Welcome"); + + METHOD(XonoticWelcomeDialog, configureDialog, void(entity)); + METHOD(XonoticWelcomeDialog, readInputArgs, void(entity, float)); + ATTRIB(XonoticWelcomeDialog, serverinfo_name, string, string_null); + ATTRIB(XonoticWelcomeDialog, serverinfo_name_ent, entity, world); + ATTRIB(XonoticWelcomeDialog, serverinfo_ip, string, string_null); + ATTRIB(XonoticWelcomeDialog, serverinfo_ip_ent, entity, world); + ATTRIB(XonoticWelcomeDialog, serverinfo_MOTD, string, string_null); + ATTRIB(XonoticWelcomeDialog, serverinfo_MOTD_ent, entity, world); + ATTRIB(XonoticWelcomeDialog, serverinfo_pic, string, string_null); + ATTRIB(XonoticWelcomeDialog, serverinfo_pic_ent, entity, NULL); +ENDCLASS(XonoticWelcomeDialog) diff --git a/qcsrc/menu/xonotic/mainwindow.qc b/qcsrc/menu/xonotic/mainwindow.qc index 0e071c2e7..895b6996c 100644 --- a/qcsrc/menu/xonotic/mainwindow.qc +++ b/qcsrc/menu/xonotic/mainwindow.qc @@ -38,6 +38,7 @@ #include "dialog_multiplayer_create_mutators.qh" #include "dialog_sandboxtools.qh" #include "dialog_monstertools.qh" +#include "dialog_welcome.qh" #include "dialog_teamselect.qh" #include "dialog_uid2name.qh" #include "dialog_singleplayer.qh" @@ -233,6 +234,10 @@ void MainWindow_configureMainWindow(entity me) i.configureDialog(i); me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS); + i = NEW(XonoticWelcomeDialog); + i.configureDialog(i); + me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z); + // main dialogs/windows me.mainNexposee = n = NEW(XonoticNexposee); diff --git a/qcsrc/menu/xonotic/textbox.qc b/qcsrc/menu/xonotic/textbox.qc new file mode 100644 index 000000000..bc86f5f7b --- /dev/null +++ b/qcsrc/menu/xonotic/textbox.qc @@ -0,0 +1,67 @@ +#include "textbox.qh" + +entity makeXonoticTextBox() +{ + entity me; + me = NEW(XonoticTextBox); + me.configureXonoticTextBox(me); + return me; +} +void XonoticTextBox_configureXonoticTextBox(entity me) +{ + me.configureListBox(me, me.scrollbarWidth, 1); // item height gets set up later +} +void XonoticTextBox_setSelected(entity me, float i) +{ + // nothing +} +void XonoticTextBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) +{ + me.itemHeight = me.rowsPerItem * me.fontSize / absSize_y; + SUPER(XonoticTextBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + + me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight); + me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth)); + me.realUpperMargin = 0.5 * (1 - me.realFontSize_y); +} +void XonoticTextBox_setText(entity me, string theText) +{ + int i, k; + string ts; + if(me.textbuf >= 0) + buf_del(me.textbuf); + me.textbuf = buf_create(); + string s = strzone(theText); + me.nItems = 0; + k = tokenizebyseparator(s, "\\n"); + for(i = 0; i < k; ++i) + { + getWrappedLine_remaining = argv(i); + if(!getWrappedLine_remaining) + { + bufstr_add(me.textbuf, "", 1); + ++me.nItems; + } + else while(getWrappedLine_remaining) + { + ts = getWrappedLine(1 - me.controlWidth, me.realFontSize, draw_TextWidth_WithColors); + if (ts != "") + { + bufstr_add(me.textbuf, ts, 1); + ++me.nItems; + } + } + } + strunzone(s); + me.textbufSize = buf_getsize(me.textbuf); +} +void XonoticTextBox_destroy(entity me) +{ + if(me.textbuf >= 0) + buf_del(me.textbuf); +} +void XonoticTextBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused) +{ + if(me.textbufSize > 0) + draw_CenterText(me.realUpperMargin * eY + 0.5 * eX, bufstr_get(me.textbuf, i), me.realFontSize, '1 1 1', 1, me.allowColors); +} diff --git a/qcsrc/menu/xonotic/textbox.qh b/qcsrc/menu/xonotic/textbox.qh new file mode 100644 index 000000000..536621b12 --- /dev/null +++ b/qcsrc/menu/xonotic/textbox.qh @@ -0,0 +1,30 @@ +#pragma once + +#include "../item/listbox.qh" +CLASS(XonoticTextBox, ListBox) + METHOD(XonoticTextBox, configureXonoticTextBox, void(entity)); + ATTRIB(XonoticTextBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticTextBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR); + ATTRIB(XonoticTextBox, src, string, SKINGFX_SCROLLBAR); + ATTRIB(XonoticTextBox, tolerance, vector, SKINTOLERANCE_SLIDER); + ATTRIB(XonoticTextBox, rowsPerItem, float, 1); + METHOD(XonoticTextBox, resizeNotify, void(entity, vector, vector, vector, vector)); + ATTRIB(XonoticTextBox, color, vector, SKINCOLOR_SCROLLBAR_N); + ATTRIB(XonoticTextBox, colorF, vector, SKINCOLOR_SCROLLBAR_F); + ATTRIB(XonoticTextBox, color2, vector, SKINCOLOR_SCROLLBAR_S); + ATTRIB(XonoticTextBox, colorC, vector, SKINCOLOR_SCROLLBAR_C); + ATTRIB(XonoticTextBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND); + ATTRIB(XonoticTextBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND); + + ATTRIB(XonoticTextBox, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticTextBox, realUpperMargin, float, 0); + + METHOD(XonoticTextBox, setSelected, void(entity, float)); + METHOD(XonoticTextBox, destroy, void(entity)); + ATTRIB(XonoticTextBox, textbuf, int, -1); + ATTRIB(XonoticTextBox, textbufSize, int, 0); + ATTRIB(XonoticTextBox, allowColors, bool, 0); + METHOD(XonoticTextBox, setText, void(entity, string)); + METHOD(XonoticTextBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused +ENDCLASS(XonoticTextBox) +entity makeXonoticTextBox(); diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index da66ece3b..814cb3278 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -812,6 +812,35 @@ void DecodeLevelParms(entity this) MUTATOR_CALLHOOK(DecodeLevelParms); } + +void serverinfo_welcomemessage_send(entity this) +{ + msg_entity = this; + WriteHeader(MSG_ONE, TE_CSQC_SERVERINFO); + WriteString(MSG_ONE, "128.03.192.999"); // FIXME: send the real server ip + WriteString(MSG_ONE, autocvar_hostname); + WriteString(MSG_ONE, getwelcomemessage(this)); + string pic = strcat("128.03.192.999", "/banner"); + if(!ImageExists(pic)) + { + print("Warning: image %s doesn't exist!\n", pic); + pic = ""; + } + WriteString(MSG_ONE, pic); +} + +void serverinfo_pic_send(entity this) +{ + string pic = strcat("128.03.192.999", "/banner"); + if(!ImageExists(pic)) + { + print("Cannot send %s, file doesn't exist!\n", pic); + return; + } + msg_entity = this; + WriteHeader(MSG_ONE, TE_CSQC_SERVERINFO_PIC); + WritePicture(MSG_ONE, pic, 3072); +} /* ============= @@ -1212,7 +1241,10 @@ void ClientConnect(entity this) this.model_randomizer = random(); if (IS_REAL_CLIENT(this)) + { + serverinfo_welcomemessage_send(this); sv_notice_join(this); + } // update physics stats (players can spawn before physics runs) Physics_UpdateStats(this, PHYS_HIGHSPEED(this)); diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index ea9610b16..516ba360d 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -133,6 +133,26 @@ void ClientCommand_clientversion(entity caller, float request, float argc) // i } } } + +void ClientCommand_getserverpic(entity caller, float request) // internal command, used only by code +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + serverinfo_pic_send(caller); + return; + } + + default: + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd getserverpic\n"); + sprint(caller, " No arguments required.\n"); + return; + } + } +} void ClientCommand_mv_getpicture(entity caller, float request, float argc) // internal command, used only by code { @@ -635,6 +655,7 @@ void ClientCommand_(entity caller, float request) CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \ CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \ CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \ + CLIENT_COMMAND("getserverpic", ClientCommand_getserverpic(ent, request), "Retrieve server banner from the server") \ CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \ CLIENT_COMMAND("physics", ClientCommand_physics(ent, request, arguments), "Change physics set") \ CLIENT_COMMAND("ready", ClientCommand_ready(ent, request), "Qualify as ready to end warmup stage (or restart server if allowed)") \ @@ -721,6 +742,7 @@ void SV_ParseClientCommand(entity this, string command) // exempt commands which are not subject to floodcheck case "begin": break; // handled by engine in host_cmd.c case "download": break; // handled by engine in cl_parse.c + case "getserverpic": break; // handled by server in this file case "mv_getpicture": break; // handled by server in this file case "pause": break; // handled by engine in host_cmd.c case "prespawn": break; // handled by engine in host_cmd.c diff --git a/qcsrc/server/command/cmd.qh b/qcsrc/server/command/cmd.qh index 5f2c86e40..337bde01c 100644 --- a/qcsrc/server/command/cmd.qh +++ b/qcsrc/server/command/cmd.qh @@ -6,5 +6,8 @@ string MapVote_Suggest(entity this, string m); +void serverinfo_welcomemessage_send(entity this); +void serverinfo_pic_send(entity this); + // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void ClientCommand_macro_write_aliases(float fh); -- 2.39.2