Skip to content

Instantly share code, notes, and snippets.

@Henningstone
Last active April 16, 2016 21:48
Show Gist options
  • Save Henningstone/c6aea68b0247a221f496 to your computer and use it in GitHub Desktop.
Save Henningstone/c6aea68b0247a221f496 to your computer and use it in GitHub Desktop.
Patch to implement tw_netgui version 0.1-b.3-rev1
From 24de4ea3e85ba68c31d3ebd110fae112a1be7f1b Mon Sep 17 00:00:00 2001
From: Henningstone <[email protected]>
Date: Mon, 28 Dec 2015 22:19:36 +0100
Subject: [PATCH] Basic NetGUI implementation
---
datasrc/network.py | 107 ++++++++++
src/engine/client/client.cpp | 8 +
src/game/client/components/menus.h | 3 +-
src/game/client/components/menus_ingame.cpp | 142 +++++++++++++
src/game/client/components/netgui.cpp | 224 ++++++++++++++++++++
src/game/client/components/netgui.h | 59 ++++++
src/game/client/gameclient.cpp | 4 +
src/game/client/gameclient.h | 1 +
src/game/netguidefines.h | 18 ++
src/game/server/gamecontext.cpp | 13 ++
src/game/server/gamecontext.h | 2 +
src/game/server/netgui.cpp | 314 ++++++++++++++++++++++++++++
src/game/server/netgui.h | 59 ++++++
src/game/version.h | 1 +
14 files changed, 954 insertions(+), 1 deletion(-)
create mode 100644 src/game/client/components/netgui.cpp
create mode 100644 src/game/client/components/netgui.h
create mode 100644 src/game/netguidefines.h
create mode 100644 src/game/server/netgui.cpp
create mode 100644 src/game/server/netgui.h
diff --git a/datasrc/network.py b/datasrc/network.py
index f62e663..11e6bdf 100644
--- a/datasrc/network.py
+++ b/datasrc/network.py
@@ -401,4 +401,111 @@ Messages = [
NetStringStrict("m_pMessage"),
]),
+
+ ### NetGUI
+ ## Server
+ NetMessage("Sv_NetGui_RemoveElement", [
+ NetIntAny("m_Type"),
+ NetIntAny("m_ID"),
+ ]),
+
+ NetMessage("Sv_NetGui_RequestData", [
+ NetIntAny("m_ID"),
+ NetIntAny("m_Type"),
+ ]),
+
+ NetMessage("Sv_NetGui_UIRect", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetArray(NetIntRange("m_Color", 0, 100), 4),
+ NetIntRange("m_Corner", 0, 255),
+ NetIntRange("m_RoundingX10", 0, 500),
+ ]),
+
+ NetMessage("Sv_NetGui_Label", [
+ NetIntAny("m_ID"),
+ NetString("m_Text"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetArray(NetIntRange("m_Color", 0, 100), 4),
+ NetIntAny("m_FontSize"),
+ NetIntRange("m_FontAlign", 0, 2),
+ NetIntRange("m_MaxTextWidth", 0, 100),
+ ]),
+
+ NetMessage("Sv_NetGui_ButtonMenu", [
+ NetIntAny("m_ID"),
+ NetString("m_Text"),
+ NetIntRange("m_Selected", 0, 1),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ ]),
+
+ NetMessage("Sv_NetGui_EditBox", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetString("m_Title"),
+ NetIntAny("m_SplitValue"),
+ NetIntAny("m_MaxTextWidth"),
+ NetIntRange("m_Password", 0, 1),
+ ]),
+
+ NetMessage("Sv_NetGui_CheckBox", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetString("m_Text"),
+ NetIntRange("m_Checked", 0, 1),
+ ]),
+
+ NetMessage("Sv_NetGui_CheckBoxNumber", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetString("m_Text"),
+ NetIntAny("m_Value"),
+ NetIntAny("m_MinValue"),
+ NetIntAny("m_MaxValue"),
+ NetIntAny("m_StepValue"),
+ ]),
+
+ NetMessage("Sv_NetGui_Scrollbar", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetIntAny("m_ValueX100"),
+ NetIntRange("m_Vertical", 0, 1),
+ ]),
+
+ NetMessage("Sv_NetGui_ScrollbarOption", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetString("m_Text"),
+ NetIntAny("m_VSplitValX10"),
+ NetIntAny("m_Value"),
+ NetIntAny("m_MinValue"),
+ NetIntAny("m_MaxValue"),
+ NetIntRange("m_Infinite", 0, 1),
+ ]),
+
+ NetMessage("Sv_NetGui_InfoBox", [
+ NetIntAny("m_ID"),
+ NetArray(NetIntRange("m_Dimension", 0, 100), 4),
+ NetString("m_Label"),
+ NetString("m_Value"),
+ ]),
+
+ ## NetGui - Client
+ NetMessage("Cl_NetGui_TriggerEvent", [
+ NetIntAny("m_Type"),
+ NetIntAny("m_ID"),
+ ]),
+
+ NetMessage("Cl_NetGui_ResponseInt", [
+ NetIntAny("m_ID"),
+ NetIntAny("m_Type"),
+ NetIntAny("m_Value"),
+ ]),
+
+ NetMessage("Cl_NetGui_ResponseString", [
+ NetIntAny("m_ID"),
+ NetIntAny("m_Type"),
+ NetString("m_Text"),
+ ]),
+
]
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index f382948..8e8a2dc 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -38,6 +38,8 @@
#include <game/version.h>
+#include <generated/protocol.h>
+
#include <mastersrv/mastersrv.h>
#include <versionsrv/versionsrv.h>
@@ -509,6 +511,12 @@ void CClient::EnterGame()
// to finish the connection
SendEnterGame();
OnEnterGame();
+
+ // tell the server that this is a netgui compatible client
+ CNetMsg_Cl_NetGui_TriggerEvent NGMsg;
+ NGMsg.m_Type = 1883;
+ NGMsg.m_ID = 5397;
+ SendPackMsg(&NGMsg, MSGFLAG_VITAL);
}
void CClient::Connect(const char *pAddress)
diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h
index 4dd5666..97d1290 100644
--- a/src/game/client/components/menus.h
+++ b/src/game/client/components/menus.h
@@ -430,7 +430,8 @@ class CMenus : public CComponent
void RenderServerControl(CUIRect MainView);
void RenderServerControlKick(CUIRect MainView, bool FilterSpectators);
void RenderServerControlServer(CUIRect MainView);
-
+ void RenderNetGui(CUIRect MainView);
+
// found in menus_browser.cpp
int m_ScrollOffset;
void RenderServerbrowserServerList(CUIRect View);
diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp
index 7044ec0..8708f64 100644
--- a/src/game/client/components/menus_ingame.cpp
+++ b/src/game/client/components/menus_ingame.cpp
@@ -20,6 +20,7 @@
#include "menus.h"
#include "motd.h"
+#include "netgui.h"
#include "voting.h"
void CMenus::RenderGame(CUIRect MainView)
@@ -184,6 +185,12 @@ void CMenus::RenderGame(CUIRect MainView)
static int s_DisconnectButton = 0;
if(DoButton_Menu(&s_DisconnectButton, Localize("Disconnect"), 0, &Right))
Client()->Disconnect();
+
+ // NetGui
+ // keep empty space at the top for notifications
+ if(!pNotification)
+ MainView.HSplitTop(45.0f, 0, &MainView);
+ RenderNetGui(MainView);
}
void CMenus::RenderPlayers(CUIRect MainView)
@@ -456,6 +463,141 @@ void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators)
m_CallvoteSelectedPlayer = Selected != -1 ? aPlayerIDs[Selected] : -1;
}
+// NetGui
+#define GUIPREPARE(name) \
+ for(int i = 0; i < m_pClient->m_pNetGui->m_NetGui##name.size(); i++)\
+ {\
+ if(i >= 1024) break;\
+ CUIRect Rect;\
+ CNetMsg_Sv_NetGui_##name *e = &m_pClient->m_pNetGui->m_NetGui##name[i];\
+ float xa = MainView.x + ((float)e->m_Dimension[0]/100.0f) * MainView.w;\
+ float xb = MainView.x + ((float)e->m_Dimension[1]/100.0f) * MainView.w;\
+ float yb = MainView.y + ((float)e->m_Dimension[2]/100.0f) * MainView.h;\
+ float ya = MainView.y + ((float)e->m_Dimension[3]/100.0f) * MainView.h;\
+ Rect.x = xa;\
+ Rect.y = ya;\
+ Rect.w = xb - xa;\
+ Rect.h = yb - ya;
+
+void CMenus::RenderNetGui(CUIRect MainView)
+{
+ // UIRect
+ GUIPREPARE(UIRect)
+ vec4 Color = vec4(
+ e->m_Color[0]/100.0f,
+ e->m_Color[1]/100.0f,
+ e->m_Color[2]/100.0f,
+ e->m_Color[3]/100.0f);
+ RenderTools()->DrawUIRect(&Rect, Color, e->m_Corner, e->m_RoundingX10/10.0f);
+ }
+
+
+ // Label
+ GUIPREPARE(Label)
+ switch(e->m_FontAlign)
+ {
+ case 1: // center
+ Rect.x += (Rect.w - TextRender()->TextWidth(0, e->m_FontSize, e->m_Text, -1))/2;
+ break;
+ case 2: // right
+ Rect.x += Rect.w - TextRender()->TextWidth(0, e->m_FontSize, e->m_Text, -1);
+ break;
+ default: // anything but 1 and 2 will result in left-aligned
+ break;
+ }
+ TextRender()->TextColor(
+ e->m_Color[0]/100.0f,
+ e->m_Color[1]/100.0f,
+ e->m_Color[2]/100.0f,
+ e->m_Color[3]/100.0f);
+
+ TextRender()->Text(0, Rect.x, Rect.y, e->m_FontSize, e->m_Text, e->m_MaxTextWidth*(int)MainView.w);
+ }
+ TextRender()->TextColor(1,1,1,1);
+
+
+ // ButtonMenu
+ GUIPREPARE(ButtonMenu)
+ static int s_ID[1024] = {0}; // nobody will create so much buttons :p
+ if(DoButton_Menu(&s_ID[i], e->m_Text, e->m_Selected, &Rect))
+ m_pClient->m_pNetGui->SendEvent(NETMSGTYPE_SV_NETGUI_BUTTONMENU, e->m_ID);
+ }
+
+
+ // EditBox
+ GUIPREPARE(EditBox)
+ static char aText[1024][1024];
+ static float s_Offset[1024] = {0};
+ static int s_ID[1024] ={0};
+ DoEditBoxOption((void *)&s_ID[i], aText[i], e->m_MaxTextWidth, &Rect, e->m_Title, ((float)e->m_SplitValue/100.0f)*Rect.w, &s_Offset[i], e->m_Password == 1 ? true : false);
+ str_copy(m_pClient->m_pNetGui->m_aNetGuiEditBoxContent[i], aText[i], sizeof(m_pClient->m_pNetGui->m_aNetGuiEditBoxContent[i]));
+ }
+
+
+ // CheckBox
+ GUIPREPARE(CheckBox)
+ static int s_ID[1024] = {0};
+ if(DoButton_CheckBox(&s_ID[i], e->m_Text, e->m_Checked, &Rect))
+ e->m_Checked ^= 1;
+ }
+
+
+ // CheckBoxNumber
+ GUIPREPARE(CheckBoxNumber)
+ static int s_ID[1024] = {0};
+ int MouseButton = DoButton_CheckBox_Number(&s_ID[i], e->m_Text, e->m_Value, &Rect);
+ if(MouseButton == 1) // primary click
+ {
+ char aBuf[128];
+ str_format(aBuf, sizeof(aBuf), "step=%d", e->m_StepValue);
+ if(e->m_Value == e->m_MaxValue)
+ e->m_Value = e->m_MinValue;
+ else if(e->m_Value + e->m_StepValue > e->m_MaxValue)
+ e->m_Value = e->m_MaxValue;
+ else
+ e->m_Value += e->m_StepValue;
+ }
+ else if(MouseButton == 2) // secondary click
+ {
+
+ if(e->m_Value == e->m_MinValue)
+ e->m_Value = e->m_MaxValue;
+ else if(e->m_Value - e->m_StepValue < e->m_MinValue)
+ e->m_Value = e->m_MinValue;
+ else
+ e->m_Value -= e->m_StepValue;
+ }
+ }
+
+
+ // Scrollbar
+ GUIPREPARE(Scrollbar)
+ static int s_ID[1024] = {0};
+ static float s_Value[1024] = {0.0f};
+ if(e->m_Vertical)
+ s_Value[i] = DoScrollbarV(&s_ID[i], &Rect, s_Value[i]);
+ else
+ s_Value[i] = DoScrollbarH(&s_ID[i], &Rect, s_Value[i]);
+
+ e->m_ValueX100 = (int)(s_Value[i] * 100.0f);
+ }
+
+
+ // ScrollbarOption
+ GUIPREPARE(ScrollbarOption)
+ static int s_ID[1024] = {0};
+ static int s_Value[1024] = {0};
+ DoScrollbarOption(&s_ID[i], &s_Value[i], &Rect, e->m_Text, (((float)e->m_VSplitValX10/10.0f)/100.0f)*Rect.w, e->m_MinValue, e->m_MaxValue, e->m_Infinite == 1 ? true : false);
+ e->m_Value = s_Value[i];
+ }
+
+
+ // InfoBox
+ GUIPREPARE(InfoBox)
+ DoInfoBox(&Rect, e->m_Label, e->m_Value);
+ }
+}
+
void CMenus::HandleCallvote(int Page, bool Force)
{
if(Page == 0)
diff --git a/src/game/client/components/netgui.cpp b/src/game/client/components/netgui.cpp
new file mode 100644
index 0000000..38c277a
--- /dev/null
+++ b/src/game/client/components/netgui.cpp
@@ -0,0 +1,224 @@
+// Copyright (c) 2015 Henritees
+
+#include "netgui.h"
+
+#define GUI_BUILDRESPONSE(name, type) \
+ int index = -1;\
+ for(int i = 0; i < m_NetGui##name.size(); i++)\
+ {\
+ if(m_NetGui##name[i].m_ID == pMsg->m_ID)\
+ index = i;\
+ }\
+ if(index < 0)\
+ break;\
+ CNetMsg_Cl_NetGui_Response##type Reply;\
+ Reply.m_ID = pMsg->m_ID;\
+ Reply.m_Type = pMsg->m_Type
+
+#define GUIRECEIVE_INIT(name) \
+ CNetMsg_Sv_NetGui_##name *pMsg = (CNetMsg_Sv_NetGui_##name *)pRawMsg;\
+ CNetMsg_Sv_NetGui_##name e;\
+ e.m_ID = pMsg->m_ID;\
+ e.m_Dimension[0] = pMsg->m_Dimension[0];\
+ e.m_Dimension[1] = pMsg->m_Dimension[1];\
+ e.m_Dimension[2] = pMsg->m_Dimension[2];\
+ e.m_Dimension[3] = pMsg->m_Dimension[3]
+
+#define GUIRECEIVE_FINALIZE(name) \
+ if(m_NetGui##name.size() > 1)\
+ {\
+ for(int i = 0; i < m_NetGui##name.size(); i++)\
+ {\
+ if(m_NetGui##name[i].m_ID == e.m_ID)\
+ m_NetGui##name.remove_index(i);\
+ }\
+ }\
+ m_NetGui##name.add(e);\
+ SortNetGuiList<CNetMsg_Sv_NetGui_##name>(m_NetGui##name)
+
+
+void CNetGui::OnReset()
+{
+ // auto-generated clear's
+ #define GUIDEFINE(name, netmsgname, args...) m_NetGui##name.clear();
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+}
+
+void CNetGui::OnMessage(int MsgId, void *pRawMsg)
+{
+ if(MsgId == NETMSGTYPE_SV_NETGUI_REMOVEELEMENT)
+ {
+ CNetMsg_Sv_NetGui_RemoveElement *pMsg = (CNetMsg_Sv_NetGui_RemoveElement *)pRawMsg;
+
+ // remove handler; the "args..." thingy is just for compatiblity and will be dropped
+ #define GUIDEFINE(name, netmsgname, args...) \
+ case NETMSGTYPE_SV_NETGUI_##netmsgname: \
+ for(int i = 0; i < m_NetGui##name.size(); i++) \
+ { \
+ if(m_NetGui##name[i].m_ID == pMsg->m_ID) \
+ m_NetGui##name.remove_index(i); \
+ } \
+ break;
+
+ switch(pMsg->m_Type)
+ {
+ // auto-generated remove handlers
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+ }
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_REQUESTDATA)
+ {
+ CNetMsg_Sv_NetGui_RequestData *pMsg = (CNetMsg_Sv_NetGui_RequestData *)pRawMsg;
+ switch(pMsg->m_Type)
+ {
+ case NETMSGTYPE_SV_NETGUI_EDITBOX:
+ {
+ GUI_BUILDRESPONSE(EditBox, String);
+ Reply.m_Text = m_aNetGuiEditBoxContent[index];
+ Client()->SendPackMsg(&Reply, MSGFLAG_VITAL);
+ }
+ break;
+ case NETMSGTYPE_SV_NETGUI_CHECKBOX:
+ {
+ GUI_BUILDRESPONSE(CheckBox, Int);
+ Reply.m_Value = m_NetGuiCheckBox[index].m_Checked;
+ Client()->SendPackMsg(&Reply, MSGFLAG_VITAL);
+ }
+ break;
+ case NETMSGTYPE_SV_NETGUI_CHECKBOXNUMBER:
+ {
+ GUI_BUILDRESPONSE(CheckBoxNumber, Int);
+ Reply.m_Value = m_NetGuiCheckBoxNumber[index].m_Value;
+ Client()->SendPackMsg(&Reply, MSGFLAG_VITAL);
+ }
+ break;
+ case NETMSGTYPE_SV_NETGUI_SCROLLBAR:
+ {
+ GUI_BUILDRESPONSE(Scrollbar, Int);
+ Reply.m_Value = m_NetGuiScrollbar[index].m_ValueX100;
+ Client()->SendPackMsg(&Reply, MSGFLAG_VITAL);
+ }
+ break;
+ case NETMSGTYPE_SV_NETGUI_SCROLLBAROPTION:
+ {
+ GUI_BUILDRESPONSE(ScrollbarOption, Int);
+ Reply.m_Value = m_NetGuiScrollbarOption[index].m_Value;
+ Client()->SendPackMsg(&Reply, MSGFLAG_VITAL);
+ }
+ break;
+
+ }
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_UIRECT)
+ {
+ GUIRECEIVE_INIT(UIRect);
+
+ e.m_Color[0] = pMsg->m_Color[0];
+ e.m_Color[1] = pMsg->m_Color[1];
+ e.m_Color[2] = pMsg->m_Color[2];
+ e.m_Color[3] = pMsg->m_Color[3];
+ e.m_Corner = pMsg->m_Corner;
+ e.m_RoundingX10 = pMsg->m_RoundingX10;
+
+ GUIRECEIVE_FINALIZE(UIRect);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_LABEL)
+ {
+ GUIRECEIVE_INIT(Label);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Text);
+ e.m_Text = aBuf;
+ e.m_Color[0] = pMsg->m_Color[0];
+ e.m_Color[1] = pMsg->m_Color[1];
+ e.m_Color[2] = pMsg->m_Color[2];
+ e.m_Color[3] = pMsg->m_Color[3];
+ e.m_FontSize = pMsg->m_FontSize;
+ e.m_FontAlign = pMsg->m_FontAlign;
+
+ GUIRECEIVE_FINALIZE(Label);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_BUTTONMENU)
+ {
+ GUIRECEIVE_INIT(ButtonMenu);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Text);
+ e.m_Text = aBuf;
+ e.m_Selected = pMsg->m_Selected;
+
+ GUIRECEIVE_FINALIZE(ButtonMenu);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_EDITBOX)
+ {
+ GUIRECEIVE_INIT(EditBox);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Title);
+ e.m_Title = aBuf;
+ e.m_SplitValue = pMsg->m_SplitValue;
+ e.m_MaxTextWidth = pMsg->m_MaxTextWidth;
+ e.m_Password = pMsg->m_Password;
+
+ GUIRECEIVE_FINALIZE(EditBox);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_CHECKBOX)
+ {
+ GUIRECEIVE_INIT(CheckBox);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Text);
+ e.m_Text = aBuf;
+ e.m_Checked = pMsg->m_Checked;
+
+ GUIRECEIVE_FINALIZE(CheckBox);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_CHECKBOXNUMBER)
+ {
+ GUIRECEIVE_INIT(CheckBoxNumber);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Text);
+ e.m_Text = aBuf;
+ e.m_Value = pMsg->m_Value;
+ e.m_MinValue = pMsg->m_MinValue;
+ e.m_MaxValue = pMsg->m_MaxValue;
+ e.m_StepValue = pMsg->m_StepValue;
+
+ GUIRECEIVE_FINALIZE(CheckBoxNumber);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_SCROLLBAR)
+ {
+ GUIRECEIVE_INIT(Scrollbar);
+ GUIRECEIVE_FINALIZE(Scrollbar);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_SCROLLBAROPTION)
+ {
+ GUIRECEIVE_INIT(ScrollbarOption);
+
+ char* aBuf = (char*)mem_alloc(512, 0);
+ str_format(aBuf, 512, "%s", pMsg->m_Text);
+ e.m_Text = aBuf;
+ e.m_Value = pMsg->m_Value;
+ e.m_MinValue = pMsg->m_MinValue;
+ e.m_MaxValue = pMsg->m_MaxValue;
+ e.m_VSplitValX10 = pMsg->m_VSplitValX10;
+
+ GUIRECEIVE_FINALIZE(ScrollbarOption);
+ }
+ else if(MsgId == NETMSGTYPE_SV_NETGUI_INFOBOX)
+ {
+ GUIRECEIVE_INIT(InfoBox);
+
+ char* aBufLabel = (char*)mem_alloc(512, 0);
+ str_format(aBufLabel, 512, "%s", pMsg->m_Label);
+ e.m_Label = aBufLabel;
+ char* aBufValue = (char*)mem_alloc(512, 0);
+ str_format(aBufValue, 512, "%s", pMsg->m_Value);
+ e.m_Value = aBufValue;
+
+ GUIRECEIVE_FINALIZE(InfoBox);
+ }
+}
diff --git a/src/game/client/components/netgui.h b/src/game/client/components/netgui.h
new file mode 100644
index 0000000..ff2f7c0
--- /dev/null
+++ b/src/game/client/components/netgui.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2015 Henritees
+
+#ifndef GAME_CLIENT_COMPONENTS_NETGUI_H
+#define GAME_CLIENT_COMPONENTS_NETGUI_H
+
+#include <base/tl/array.h>
+#include <game/client/component.h>
+
+class CNetGui : public CComponent
+{
+ virtual void OnReset();
+ virtual void OnMessage(int MsgId, void *pRawMsg);
+
+public:
+ // automatically make a storage array for everything
+ #define GUIDEFINE(name, netmsgname, args...) array<CNetMsg_Sv_NetGui_##name> m_NetGui##name;
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+
+ char m_aNetGuiEditBoxContent[1024][1024]; // necessary because it cannot be stored into the NetMsg (CONST char...)
+
+
+ // maxsort to get stuff into correct render order.
+ template<class T>
+ void SortNetGuiList(array<T> &elem)
+ {
+ int num = elem.size();
+ if(num < 2)
+ return;
+ int max = 0;
+ T temp;
+ for(int i = 0; i < max; i++)
+ {
+ max = i;
+ for(int j = i; j < num; j++)
+ {
+ if(elem[i].m_ID > elem[max].m_ID)
+ max = i;
+ }
+
+ if(max == i)
+ continue;
+ temp = elem[i];
+ elem[i] = elem[max];
+ elem[max] = temp;
+ }
+ }
+
+ void SendEvent(int Type, int NetGuiElementID)
+ {
+ CNetMsg_Cl_NetGui_TriggerEvent Msg;
+ Msg.m_Type = Type;
+ Msg.m_ID = NetGuiElementID;
+ Client()->SendPackMsg(&Msg, MSGFLAG_VITAL);
+ }
+
+};
+
+#endif
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index cd58061..c34a8a4 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -41,6 +41,7 @@
#include "components/maplayers.h"
#include "components/menus.h"
#include "components/motd.h"
+#include "components/netgui.h"
#include "components/particles.h"
#include "components/players.h"
#include "components/nameplates.h"
@@ -60,6 +61,7 @@ static CGameConsole gs_GameConsole;
static CBinds gs_Binds;
static CParticles gs_Particles;
static CMenus gs_Menus;
+static CNetGui gs_NetGui;
static CSkins gs_Skins;
static CCountryFlags gs_CountryFlags;
static CFlow gs_Flow;
@@ -159,6 +161,7 @@ void CGameClient::OnConsoleInit()
m_pGameConsole = &::gs_GameConsole;
m_pParticles = &::gs_Particles;
m_pMenus = &::gs_Menus;
+ m_pNetGui = &::gs_NetGui;
m_pSkins = &::gs_Skins;
m_pCountryFlags = &::gs_CountryFlags;
m_pChat = &::gs_Chat;
@@ -206,6 +209,7 @@ void CGameClient::OnConsoleInit()
m_All.Add(&gs_DebugHud);
m_All.Add(&gs_Scoreboard);
m_All.Add(m_pMotd);
+ m_All.Add(m_pNetGui);
m_All.Add(m_pMenus);
m_All.Add(m_pGameConsole);
diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h
index 3735bf2..2d861fb 100644
--- a/src/game/client/gameclient.h
+++ b/src/game/client/gameclient.h
@@ -266,6 +266,7 @@ public:
class CBroadcast *m_pBroadcast;
class CParticles *m_pParticles;
class CMenus *m_pMenus;
+ class CNetGui *m_pNetGui;
class CSkins *m_pSkins;
class CCountryFlags *m_pCountryFlags;
class CFlow *m_pFlow;
diff --git a/src/game/netguidefines.h b/src/game/netguidefines.h
new file mode 100644
index 0000000..5a63d30
--- /dev/null
+++ b/src/game/netguidefines.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2015 Henritees
+#ifndef GAME_NETGUIDEFINES_H
+#define GAME_NETGUIDEFINES_H
+#undef GAME_NETGUIDEFINES_H // this file will be included several times
+
+#ifdef GUIDEFINE
+GUIDEFINE(UIRect, UIRECT, vec4 Color, int Corner, float Rounding);
+GUIDEFINE(Label, LABEL, const char *pText, vec4 Color, int FontSize, int FontAlign, int MaxTextWidth);
+GUIDEFINE(ButtonMenu, BUTTONMENU, const char *pText, bool Selected);
+GUIDEFINE(EditBox, EDITBOX, const char *pTitle, int SplitValue, int MaxTextWidth, bool Password);
+GUIDEFINE(CheckBox, CHECKBOX, const char *pText, bool Checked);
+GUIDEFINE(CheckBoxNumber, CHECKBOXNUMBER, const char *pText, int MinValue, int MaxValue, int StepValue);
+GUIDEFINE(Scrollbar, SCROLLBAR, bool Vertical = false);
+GUIDEFINE(ScrollbarOption, SCROLLBAROPTION, const char *pText, float VSplitVal, int Min, int Max, bool Infinite);
+GUIDEFINE(InfoBox, INFOBOX, const char *pLabel, const char* pValue);
+#endif
+
+#endif
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index d3ef828..cb3fbdd 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -31,6 +31,7 @@ void CGameContext::Construct(int Resetting)
{
m_Resetting = 0;
m_pServer = 0;
+ m_pNetGui = 0;
for(int i = 0; i < MAX_CLIENTS; i++)
m_apPlayers[i] = 0;
@@ -44,7 +45,10 @@ void CGameContext::Construct(int Resetting)
m_LockTeams = 0;
if(Resetting==NO_RESET)
+ {
m_pVoteOptionHeap = new CHeap();
+ m_pNetGui = new CNetGui(this);
+ }
}
CGameContext::CGameContext(int Resetting)
@@ -62,7 +66,10 @@ CGameContext::~CGameContext()
for(int i = 0; i < MAX_CLIENTS; i++)
delete m_apPlayers[i];
if(!m_Resetting)
+ {
delete m_pVoteOptionHeap;
+ delete m_pNetGui;
+ }
}
void CGameContext::Clear()
@@ -72,6 +79,7 @@ void CGameContext::Clear()
CVoteOptionServer *pVoteOptionLast = m_pVoteOptionLast;
int NumVoteOptions = m_NumVoteOptions;
CTuningParams Tuning = m_Tuning;
+ CNetGui *pNetGui = m_pNetGui;
m_Resetting = true;
this->~CGameContext();
@@ -83,6 +91,7 @@ void CGameContext::Clear()
m_pVoteOptionLast = pVoteOptionLast;
m_NumVoteOptions = NumVoteOptions;
m_Tuning = Tuning;
+ m_pNetGui = pNetGui;
}
@@ -688,6 +697,8 @@ void CGameContext::OnClientDrop(int ClientID, const char *pReason)
m_apPlayers[ClientID] = 0;
m_VoteUpdate = true;
+
+ m_pNetGui->OnClientDrop(ClientID);
}
void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
@@ -708,6 +719,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
if(Server()->ClientIngame(ClientID))
{
+ m_pNetGui->OnMessage(MsgID, pRawMsg, ClientID);
+
if(MsgID == NETMSGTYPE_CL_SAY)
{
if(g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat+Server()->TickSpeed() > Server()->Tick())
diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h
index c6e4935..b088455 100644
--- a/src/game/server/gamecontext.h
+++ b/src/game/server/gamecontext.h
@@ -11,6 +11,7 @@
#include "eventhandler.h"
#include "gameworld.h"
+#include "netgui.h"
/*
Tick
@@ -41,6 +42,7 @@ class CGameContext : public IGameServer
CCollision m_Collision;
CNetObjHandler m_NetObjHandler;
CTuningParams m_Tuning;
+ CNetGui *m_pNetGui;
static void ConTuneParam(IConsole::IResult *pResult, void *pUserData);
static void ConTuneReset(IConsole::IResult *pResult, void *pUserData);
diff --git a/src/game/server/netgui.cpp b/src/game/server/netgui.cpp
new file mode 100644
index 0000000..c1b5ede
--- /dev/null
+++ b/src/game/server/netgui.cpp
@@ -0,0 +1,314 @@
+// Copyright (c) 2015 Henritees
+
+#include <game/server/gamecontext.h>
+#include <game/server/gamecontroller.h>
+#include <engine/server.h>
+
+#include "netgui.h"
+
+#define PREBUILD(msgtype) \
+ CNetMsg_Sv_NetGui_##msgtype Msg; \
+ Msg.m_ID = NetGuiElemID; \
+ Msg.m_Dimension[0] = Dimensions.x; \
+ Msg.m_Dimension[1] = Dimensions.y; \
+ Msg.m_Dimension[2] = Dimensions.a; \
+ Msg.m_Dimension[3] = Dimensions.b
+
+
+// ----------------------------- [start of GUI managing methods] -----------------------------
+void CNetGui::CreateGui_EmptyGui(int ClientID)
+{
+
+}
+void CNetGui::RemoveGui_EmptyGui(int ClientID)
+{
+
+}
+
+// ------------------------------ [end of GUI managing methods] -----------------------------
+
+void CNetGui::OnClientEnter(int ClientID)
+{
+}
+
+void CNetGui::OnClientCompatible(int ClientID)
+{
+
+}
+
+void CNetGui::OnClientDrop(int ClientID)
+{
+ m_NetGuiClients[ClientID] = false;
+ // auto-generated clear's
+ #define GUIDEFINE(name, netmsgname, args...) m_##name[ClientID].clear();
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+}
+
+void CNetGui::OnMessage(int MsgID, void *pRawMsg, int ClientID)
+{
+ CPlayer *pPlayer = GameServer()->m_apPlayers[ClientID];
+
+ if (MsgID == NETMSGTYPE_CL_NETGUI_TRIGGEREVENT)
+ {
+ CNetMsg_Cl_NetGui_TriggerEvent *pMsg = (CNetMsg_Cl_NetGui_TriggerEvent *)pRawMsg;
+
+ switch(pMsg->m_Type)
+ {
+ case NETGUIMAGICNUMBER1: // verify compatibility
+ {
+ if(pMsg->m_ID == NETGUIMAGICNUMBER2)
+ {
+ dbg_msg("netgui", "client ID:%d has proven netgui compatibility (%d)", ClientID, pMsg->m_ID);
+ m_NetGuiClients[ClientID] = true;
+
+ // this seems to be the earliest moment possible for sending the first GUI...
+ OnClientCompatible(ClientID);
+ }
+ else
+ {
+ GameServer()->SendChatTarget(ClientID, "[NetGUI] Your client has only partially proven NetGUI ability, you won't receive GUIs.");
+ GameServer()->SendChatTarget(ClientID, "[NetGUI] You may ask the server owner for further instructions. Maybe it's time for an update?");
+ dbg_msg("netgui", "client ID:%d has only partially proven netgui ability, what went wrong? (%d != %d)", ClientID, pMsg->m_ID, NETGUIMAGICNUMBER2);
+ }
+ }
+ break;
+ case NETMSGTYPE_SV_NETGUI_BUTTONMENU:
+ bool exists = false;
+ for(int i = 0; i < m_ButtonMenu[ClientID].size(); i++)
+ {
+ if(m_ButtonMenu[ClientID][i].m_ID == pMsg->m_ID)
+ exists = true;
+ }
+ if(exists)
+ {
+ switch(pMsg->m_ID)
+ {
+ // TODO: handle button presses
+ }
+ }
+ break;
+ }
+ }
+ else if(MsgID == NETMSGTYPE_CL_NETGUI_RESPONSESTRING)
+ {
+ CNetMsg_Cl_NetGui_ResponseString *pMsg = (CNetMsg_Cl_NetGui_ResponseString *)pRawMsg;
+
+ switch(pMsg->m_Type)
+ {
+ // TODO: handle string responses
+ }
+ }
+ else if(MsgID == NETMSGTYPE_CL_NETGUI_RESPONSEINT)
+ {
+ CNetMsg_Cl_NetGui_ResponseInt *pMsg = (CNetMsg_Cl_NetGui_ResponseInt *)pRawMsg;
+
+ switch(pMsg->m_Type)
+ {
+ // TODO: handle integer responses
+ }
+ }
+}
+
+void CNetGui::RemoveElement(int ClientID, int Type, int NetGuiElemID)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ CNetMsg_Sv_NetGui_RemoveElement Msg;
+ Msg.m_Type = Type;
+ Msg.m_ID = NetGuiElemID;
+
+ // remove handler; the "args..." thingy is just for compatiblity and will be dropped
+ #define GUIDEFINE(name, netmsgname, args...) \
+ case NETMSGTYPE_SV_NETGUI_##netmsgname: \
+ for(int i = 0; i < m_##name[ClientID].size(); i++) \
+ { \
+ if(m_##name[ClientID][i].m_ID == NetGuiElemID) \
+ m_##name[ClientID].remove_index(i); \
+ } \
+ break;
+
+
+ switch(Type)
+ {
+ // auto-generated remove handlers
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+ }
+
+ SendNetGui(ClientID, Msg);
+}
+
+
+void CNetGui::DoUIRect(int ClientID, int NetGuiElemID, vec4 Dimensions, vec4 Color, int Corner, float Rounding)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(UIRect);
+ Msg.m_Color[0] = Color.r;
+ Msg.m_Color[1] = Color.g;
+ Msg.m_Color[2] = Color.b;
+ Msg.m_Color[3] = Color.a;
+ Msg.m_Corner = Corner;
+ Msg.m_RoundingX10 = (int)(Rounding*10.0f);
+
+ m_UIRect[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoLabel(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pText, vec4 Color, int FontSize, int FontAlign, int MaxTextWidth)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(Label);
+ Msg.m_Text = pText;
+ Msg.m_Color[0] = Color.r;
+ Msg.m_Color[1] = Color.g;
+ Msg.m_Color[2] = Color.b;
+ Msg.m_Color[3] = Color.a;
+ Msg.m_FontSize = FontSize;
+ Msg.m_FontAlign = FontAlign;
+ Msg.m_MaxTextWidth = MaxTextWidth;
+
+ m_Label[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoButtonMenu(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pText, bool Selected)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(ButtonMenu);
+ Msg.m_Text = pText;
+ Msg.m_Selected = Selected ? 1 : 0;
+
+ m_ButtonMenu[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoEditBox(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pTitle, int SplitValue, int MaxTextWidth, bool Password)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(EditBox);
+ Msg.m_Title = pTitle;
+ Msg.m_SplitValue = SplitValue;
+ Msg.m_MaxTextWidth = MaxTextWidth;
+ Msg.m_Password = Password ? 1 : 0;
+
+ m_EditBox[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoCheckBox(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pText, bool Checked)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(CheckBox);
+ Msg.m_Text = pText;
+ Msg.m_Checked = Checked ? 1 : 0;
+
+ m_CheckBox[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoCheckBoxNumber(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pText, int MinValue, int MaxValue, int StepValue)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(CheckBoxNumber);
+ Msg.m_Text = pText;
+ Msg.m_Value = MinValue;
+ Msg.m_MinValue = MinValue;
+ Msg.m_MaxValue = MaxValue;
+ Msg.m_StepValue = StepValue;
+
+ m_CheckBoxNumber[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoScrollbar(int ClientID, int NetGuiElemID, vec4 Dimensions, bool Vertical)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(Scrollbar);
+ Msg.m_Vertical = Vertical ? 1 : 0;
+
+ m_Scrollbar[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoScrollbarOption(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pText, float VSplitVal, int Min, int Max, bool Infinite)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(ScrollbarOption);
+ Msg.m_Text = pText;
+ Msg.m_VSplitValX10 = (int)(VSplitVal*10.0f);
+ Msg.m_MinValue = Min;
+ Msg.m_MaxValue = Max;
+ Msg.m_Infinite = Infinite ? 1 : 0;
+
+ m_ScrollbarOption[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::DoInfoBox(int ClientID, int NetGuiElemID, vec4 Dimensions, const char *pLabel, const char* pValue)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ PREBUILD(InfoBox);
+ Msg.m_Label = pLabel;
+ Msg.m_Value = pValue;
+
+ m_InfoBox[ClientID].add(Msg);
+
+ SendNetGui(ClientID, Msg);
+}
+
+void CNetGui::RequestData(int ClientID, int Type, int NetGuiElemID)
+{
+ if(!m_NetGuiClients[ClientID])
+ return;
+
+ CNetMsg_Sv_NetGui_RequestData Msg;
+ Msg.m_ID = NetGuiElemID;
+ Msg.m_Type = Type;
+ GameServer()->Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID);
+}
+
+
+template<class T>
+void CNetGui::SendNetGui(int ClientID, T Msg)
+{
+ if(ClientID < 0)
+ {
+ for(int i = 0; i < MAX_CLIENTS; ++i)
+ {
+ if(!GameServer()->m_apPlayers[i])
+ continue;
+
+ GameServer()->Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, i);
+ }
+ }
+ else
+ GameServer()->Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID);
+}
diff --git a/src/game/server/netgui.h b/src/game/server/netgui.h
new file mode 100644
index 0000000..ddb4232
--- /dev/null
+++ b/src/game/server/netgui.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2015 Henritees
+
+#ifndef GAME_SERVER_NETGUI_H
+#define GAME_SERVER_NETGUI_H
+
+#include <base/tl/array.h>
+#include <game/server/gamecontroller.h>
+#include <game/server/gamecontext.h>
+#include <engine/shared/protocol.h>
+#include <engine/server.h>
+
+#include "player.h"
+
+#define NETGUIMAGICNUMBER1 1883
+#define NETGUIMAGICNUMBER2 5397
+
+#define GUISET(name) void CreateGui_##name(int ClientID); void RemoveGui_##name(int ClientID);
+#define GUIDEFINE(name, netmsgname, args...) \
+ public: \
+ void Do##name(int ClientID, int NetGuiElemID, vec4 Dimensions, args); \
+ private: \
+ array<CNetMsg_Sv_NetGui_##name> m_##name[MAX_CLIENTS]; public:
+
+class CNetGui
+{
+ CGameContext *m_pGameServer;
+
+ // methods for managing sets of GUIs
+ GUISET(EmptyGui)
+
+public:
+ CNetGui(CGameContext *pGameServer) : m_pGameServer(pGameServer){}
+ void RemoveElement(int ClientID, int Type, int NetGuiElemID);
+
+ void OnClientEnter(int ClientID);
+ void OnClientDrop(int ClientID);
+ void OnClientCompatible(int ClientID);
+ void OnMessage(int MsgID, void *pRawMsg, int ClientID);
+
+ bool IsNetGuiClient(int ClientID) { return m_NetGuiClients[ClientID]; }
+
+ // // auto-generated declarations of functions
+ #include <game/netguidefines.h>
+ #undef GUIDEFINE
+
+protected:
+ CGameContext *GameServer() const { return m_pGameServer; }
+
+private:
+ bool m_NetGuiClients[MAX_CLIENTS]; // could have been put into CPlayer as well, but I want to keep stuff together
+
+ template<class T>
+ void SendNetGui(int ClientID, T Msg);
+ void RequestData(int ClientID, int Type, int NetGuiElemID);
+
+
+};
+
+#endif
diff --git a/src/game/version.h b/src/game/version.h
index e5640ee..14f3451 100644
--- a/src/game/version.h
+++ b/src/game/version.h
@@ -5,5 +5,6 @@
#include <generated/nethash.cpp>
#define GAME_VERSION "0.7 trunk"
#define GAME_NETVERSION "0.7 " GAME_NETVERSION_HASH
+#define GAME_NETGUI_VERSION "0.7-0.1.b20151228"
static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '1', 0};
#endif
--
2.5.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment