From fae0115bd824a56f949fdb927dd0c229c8c50275 Mon Sep 17 00:00:00 2001 From: oy Date: Sun, 1 Nov 2015 20:05:26 +0100 Subject: [PATCH] don't reload serverlist when switching between lan and global. Closes #1281 --- src/engine/client/serverbrowser.cpp | 229 ++++++++++++------- src/engine/client/serverbrowser.h | 43 ++-- src/engine/serverbrowser.h | 9 +- src/game/client/components/menus.cpp | 10 +- src/game/client/components/menus_browser.cpp | 4 +- 5 files changed, 182 insertions(+), 113 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 2f284dac7..2406fb7d9 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -33,13 +33,27 @@ inline int GetNewToken() return random_int() & 0x7FFFFFFF; } +// +void CServerBrowser::CServerlist::Clear() +{ + m_ServerlistHeap.Reset(); + m_NumServers = 0; + m_NumPlayers = 0; + mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp)); +} + // CServerBrowser::CServerBrowser() { m_pMasterServer = 0; // - mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp)); + for(int i = 0; i < NUM_TYPES; ++i) + { + m_aServerlist[i].Clear(); + m_aServerlist[i].m_NumServerCapacity = 0; + m_aServerlist[i].m_ppServerlist = 0; + } m_pFirstReqServer = 0; // request list m_pLastReqServer = 0; @@ -47,15 +61,10 @@ CServerBrowser::CServerBrowser() m_NeedRefresh = 0; - m_NumServers = 0; - m_NumServerCapacity = 0; - - m_NumPlayers = 0; - // the token is to keep server refresh separated from each other m_CurrentLanToken = 1; - m_ServerlistType = 0; + m_ActServerlistType = 0; m_BroadcastTime = 0; } @@ -69,20 +78,23 @@ void CServerBrowser::Init(class CNetClient *pNetClient, const char *pNetVersion) m_ServerBrowserFilter.Init(Kernel()->RequestInterface(), pNetVersion); } -void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo) +void CServerBrowser::Set(const NETADDR &Addr, int SetType, int Token, const CServerInfo *pInfo) { CServerEntry *pEntry = 0; - if(Type == SET_MASTER_ADD) + switch(SetType) { - if(m_ServerlistType != IServerBrowser::TYPE_INTERNET) - return; - - if(!Find(Addr)) + case SET_MASTER_ADD: { - pEntry = Add(Addr); - QueueRequest(pEntry); + if(!(m_RefreshFlags&IServerBrowser::REFRESHFLAG_INTERNET)) + return; + + if(!Find(IServerBrowser::TYPE_INTERNET, Addr)) + { + pEntry = Add(IServerBrowser::TYPE_INTERNET, Addr); + QueueRequest(pEntry); + } } - } + break; /*else if(Type == SET_FAV_ADD) { if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES) @@ -94,26 +106,41 @@ void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServer QueueRequest(pEntry); } }*/ - else if(Type == SET_TOKEN) - { - if(m_ServerlistType == IServerBrowser::TYPE_LAN && Token != m_CurrentLanToken) - return; - - pEntry = Find(Addr); - if(!pEntry && m_ServerlistType == IServerBrowser::TYPE_LAN && m_BroadcastTime+time_freq() >= time_get()) - pEntry = Add(Addr); - if(pEntry && ((pEntry->m_InfoState == CServerEntry::STATE_PENDING && Token == pEntry->m_CurrentToken) || m_ServerlistType == IServerBrowser::TYPE_LAN)) + case SET_TOKEN: { - SetInfo(pEntry, *pInfo); - if(m_ServerlistType == IServerBrowser::TYPE_LAN) - pEntry->m_Info.m_Latency = min(static_cast((time_get()-m_BroadcastTime)*1000/time_freq()), 999); - else - pEntry->m_Info.m_Latency = min(static_cast((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999); - RemoveRequest(pEntry); + int Type; + + // internet entry + if(m_RefreshFlags&IServerBrowser::REFRESHFLAG_INTERNET) + { + Type = IServerBrowser::TYPE_INTERNET; + pEntry = Find(Type, Addr); + if(pEntry && (pEntry->m_InfoState != CServerEntry::STATE_PENDING || Token != pEntry->m_CurrentToken)) + pEntry = 0; + } + + // lan entry + if(!pEntry && (m_RefreshFlags&IServerBrowser::REFRESHFLAG_LAN) && m_BroadcastTime+time_freq() >= time_get()) + { + Type = IServerBrowser::TYPE_LAN; + pEntry = Add(Type, Addr); + } + + // set info + if(pEntry) + { + SetInfo(Type, pEntry, *pInfo); + if(Type == IServerBrowser::TYPE_LAN) + pEntry->m_Info.m_Latency = min(static_cast((time_get()-m_BroadcastTime)*1000/time_freq()), 999); + else + pEntry->m_Info.m_Latency = min(static_cast((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999); + RemoveRequest(pEntry); + } } } - m_ServerBrowserFilter.Sort(m_ppServerlist, m_NumServers, CServerBrowserFilter::RESORT_FLAG_FORCE); + if(pEntry) + m_ServerBrowserFilter.Sort(m_aServerlist[m_ActServerlistType].m_ppServerlist, m_aServerlist[m_ActServerlistType].m_NumServers, CServerBrowserFilter::RESORT_FLAG_FORCE); } void CServerBrowser::Update(bool ForceResort) @@ -190,35 +217,44 @@ void CServerBrowser::Update(bool ForceResort) const NETADDR *pFavAddr = m_ServerBrowserFavorites.UpdateFavorites(); if(pFavAddr) { - CServerEntry *pEntry = Find(*pFavAddr); - if(pEntry) - pEntry->m_Info.m_Favorite = 1; + for(int i = 0; i < NUM_TYPES; ++i) + { + CServerEntry *pEntry = Find(i, *pFavAddr); + if(pEntry) + pEntry->m_Info.m_Favorite = 1; + + if(i == m_ActServerlistType) + ForceResort = true; + } } - m_ServerBrowserFilter.Sort(m_ppServerlist, m_NumServers, ForceResort ? CServerBrowserFilter::RESORT_FLAG_FORCE : 0); + m_ServerBrowserFilter.Sort(m_aServerlist[m_ActServerlistType].m_ppServerlist, m_aServerlist[m_ActServerlistType].m_NumServers, ForceResort ? CServerBrowserFilter::RESORT_FLAG_FORCE : 0); } // interface functions -void CServerBrowser::Refresh(int Type) +void CServerBrowser::SetType(int Type) { - // clear out everything - m_ServerlistHeap.Reset(); - m_NumServers = 0; - m_NumPlayers = 0; - m_ServerBrowserFilter.Clear(); - mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp)); - m_pFirstReqServer = 0; - m_pLastReqServer = 0; - m_NumRequests = 0; + if(Type < 0 || Type >= NUM_TYPES && Type != m_ActServerlistType) + return; - // next token - m_CurrentLanToken = GetNewToken(); + m_ActServerlistType = Type; + m_ServerBrowserFilter.Sort(m_aServerlist[m_ActServerlistType].m_ppServerlist, m_aServerlist[m_ActServerlistType].m_NumServers, CServerBrowserFilter::RESORT_FLAG_FORCE); +} - // - m_ServerlistType = Type; +void CServerBrowser::Refresh(int RefreshFlags) +{ + m_RefreshFlags = RefreshFlags; - if(Type == IServerBrowser::TYPE_LAN) + if(RefreshFlags&IServerBrowser::REFRESHFLAG_LAN) { + // clear out everything + m_aServerlist[IServerBrowser::TYPE_LAN].Clear(); + if(m_ActServerlistType == IServerBrowser::TYPE_LAN) + m_ServerBrowserFilter.Clear(); + + // next token + m_CurrentLanToken = GetNewToken(); + CPacker Packer; Packer.Reset(); Packer.AddRaw(SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); @@ -243,8 +279,19 @@ void CServerBrowser::Refresh(int Type) if(g_Config.m_Debug) m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "broadcasting for servers"); } - else if(Type == IServerBrowser::TYPE_INTERNET) + + if(RefreshFlags&IServerBrowser::REFRESHFLAG_INTERNET) + { + // clear out everything + m_aServerlist[IServerBrowser::TYPE_INTERNET].Clear(); + if(m_ActServerlistType == IServerBrowser::TYPE_INTERNET) + m_ServerBrowserFilter.Clear(); + m_pFirstReqServer = 0; + m_pLastReqServer = 0; + m_NumRequests = 0; + m_NeedRefresh = 1; + } /*else if(Type == IServerBrowser::TYPE_FAVORITES) { for(int i = 0; i < m_NumFavoriteServers; i++) @@ -255,11 +302,11 @@ void CServerBrowser::Refresh(int Type) int CServerBrowser::LoadingProgression() const { - if(m_NumServers == 0) + if(m_aServerlist[m_ActServerlistType].m_NumServers == 0) return 0; - int Servers = m_NumServers; - int Loaded = m_NumServers-m_NumRequests; + int Servers = m_aServerlist[m_ActServerlistType].m_NumServers; + int Loaded = m_aServerlist[m_ActServerlistType].m_NumServers-m_NumRequests; return 100.0f * Loaded/Servers; } @@ -267,12 +314,18 @@ void CServerBrowser::AddFavorite(const CServerInfo *pInfo) { if(m_ServerBrowserFavorites.AddFavoriteEx(pInfo->m_aHostname, &pInfo->m_NetAddr, true)) { - CServerEntry *pEntry = Find(pInfo->m_NetAddr); - if(pEntry) - pEntry->m_Info.m_Favorite = 1; + for(int i = 0; i < NUM_TYPES; ++i) + { + CServerEntry *pEntry = Find(i, pInfo->m_NetAddr); + if(pEntry) + { + pEntry->m_Info.m_Favorite = 1; - // refresh servers in all filters where favorites are filtered - m_ServerBrowserFilter.Sort(m_ppServerlist, m_NumServers, CServerBrowserFilter::RESORT_FLAG_FAV); + // refresh servers in all filters where favorites are filtered + if(i == m_ActServerlistType) + m_ServerBrowserFilter.Sort(m_aServerlist[m_ActServerlistType].m_ppServerlist, m_aServerlist[m_ActServerlistType].m_NumServers, CServerBrowserFilter::RESORT_FLAG_FAV); + } + } } } @@ -280,20 +333,26 @@ void CServerBrowser::RemoveFavorite(const CServerInfo *pInfo) { if(m_ServerBrowserFavorites.RemoveFavoriteEx(pInfo->m_aHostname, &pInfo->m_NetAddr)) { - CServerEntry *pEntry = Find(pInfo->m_NetAddr); - if(pEntry) - pEntry->m_Info.m_Favorite = 0; + for(int i = 0; i < NUM_TYPES; ++i) + { + CServerEntry *pEntry = Find(i, pInfo->m_NetAddr); + if(pEntry) + { + pEntry->m_Info.m_Favorite = 0; - // refresh servers in all filters where favorites are filtered - m_ServerBrowserFilter.Sort(m_ppServerlist, m_NumServers, CServerBrowserFilter::RESORT_FLAG_FAV); + // refresh servers in all filters where favorites are filtered + if(i == m_ActServerlistType) + m_ServerBrowserFilter.Sort(m_aServerlist[m_ActServerlistType].m_ppServerlist, m_aServerlist[m_ActServerlistType].m_NumServers, CServerBrowserFilter::RESORT_FLAG_FAV); + } + } } } // manipulate entries -CServerEntry *CServerBrowser::Add(const NETADDR &Addr) +CServerEntry *CServerBrowser::Add(int ServerlistType, const NETADDR &Addr) { // create new pEntry - CServerEntry *pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry)); + CServerEntry *pEntry = (CServerEntry *)m_aServerlist[ServerlistType].m_ServerlistHeap.Allocate(sizeof(CServerEntry)); mem_zero(pEntry, sizeof(CServerEntry)); // set the info @@ -313,39 +372,39 @@ CServerEntry *CServerBrowser::Add(const NETADDR &Addr) // add to the hash list int Hash = AddrHash(&Addr); - pEntry->m_pNextIp = m_aServerlistIp[Hash]; - m_aServerlistIp[Hash] = pEntry; + pEntry->m_pNextIp = m_aServerlist[ServerlistType].m_aServerlistIp[Hash]; + m_aServerlist[ServerlistType].m_aServerlistIp[Hash] = pEntry; - if(m_NumServers == m_NumServerCapacity) + if(m_aServerlist[ServerlistType].m_NumServers == m_aServerlist[ServerlistType].m_NumServerCapacity) { - if(m_NumServerCapacity == 0) + if(m_aServerlist[ServerlistType].m_NumServerCapacity == 0) { // alloc start size - m_NumServerCapacity = 1000; - m_ppServerlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1); + m_aServerlist[ServerlistType].m_NumServerCapacity = 1000; + m_aServerlist[ServerlistType].m_ppServerlist = (CServerEntry **)mem_alloc(m_aServerlist[ServerlistType].m_NumServerCapacity*sizeof(CServerEntry*), 1); } else { // increase size - m_NumServerCapacity += 100; - CServerEntry **ppNewlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1); - mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*)); - mem_free(m_ppServerlist); - m_ppServerlist = ppNewlist; + m_aServerlist[ServerlistType].m_NumServerCapacity += 100; + CServerEntry **ppNewlist = (CServerEntry **)mem_alloc(m_aServerlist[ServerlistType].m_NumServerCapacity*sizeof(CServerEntry*), 1); + mem_copy(ppNewlist, m_aServerlist[ServerlistType].m_ppServerlist, m_aServerlist[ServerlistType].m_NumServers*sizeof(CServerEntry*)); + mem_free(m_aServerlist[ServerlistType].m_ppServerlist); + m_aServerlist[ServerlistType].m_ppServerlist = ppNewlist; } } // add to list - m_ppServerlist[m_NumServers] = pEntry; - pEntry->m_Info.m_ServerIndex = m_NumServers; - m_NumServers++; + m_aServerlist[ServerlistType].m_ppServerlist[m_aServerlist[ServerlistType].m_NumServers] = pEntry; + pEntry->m_Info.m_ServerIndex = m_aServerlist[ServerlistType].m_NumServers; + m_aServerlist[ServerlistType].m_NumServers++; return pEntry; } -CServerEntry *CServerBrowser::Find(const NETADDR &Addr) +CServerEntry *CServerBrowser::Find(int ServerlistType, const NETADDR &Addr) { - for(CServerEntry *pEntry = m_aServerlistIp[AddrHash(&Addr)]; pEntry; pEntry = pEntry->m_pNextIp) + for(CServerEntry *pEntry = m_aServerlist[ServerlistType].m_aServerlistIp[AddrHash(&Addr)]; pEntry; pEntry = pEntry->m_pNextIp) { if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0) return pEntry; @@ -418,7 +477,7 @@ void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) cons } } -void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info) +void CServerBrowser::SetInfo(int ServerlistType, CServerEntry *pEntry, const CServerInfo &Info) { int Fav = pEntry->m_Info.m_Favorite; pEntry->m_Info = Info; @@ -435,7 +494,7 @@ void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info) pEntry->m_Info.m_Favorite = Fav; pEntry->m_Info.m_NetAddr = pEntry->m_Addr; - m_NumPlayers += pEntry->m_Info.m_NumPlayers; + m_aServerlist[ServerlistType].m_NumPlayers += pEntry->m_Info.m_NumPlayers; pEntry->m_InfoState = CServerEntry::STATE_READY; } diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index 9fdc1aeb3..e8860206b 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -20,21 +20,22 @@ public: CServerBrowser(); void Init(class CNetClient *pClient, const char *pNetVersion); - void Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo); + void Set(const NETADDR &Addr, int SetType, int Token, const CServerInfo *pInfo); void Update(bool ForceResort); // interface functions - void Refresh(int Type); + void SetType(int Type); + void Refresh(int RefreshFlags); bool IsRefreshing() const { return m_pFirstReqServer != 0; } bool IsRefreshingMasters() const { return m_pMasterServer->IsRefreshing(); } int LoadingProgression() const; - int NumServers() const { return m_NumServers; } - int NumPlayers() const { return m_NumPlayers; } + int NumServers() const { return m_aServerlist[m_ActServerlistType].m_NumServers; } + int NumPlayers() const { return m_aServerlist[m_ActServerlistType].m_NumPlayers; } int NumSortedServers(int Index) const { return m_ServerBrowserFilter.GetNumSortedServers(Index); } int NumSortedPlayers(int Index) const { return m_ServerBrowserFilter.GetNumSortedPlayers(Index); } - const CServerInfo *SortedGet(int FilterIndex, int Index) const { return &m_ppServerlist[m_ServerBrowserFilter.GetIndex(FilterIndex, Index)]->m_Info; }; + const CServerInfo *SortedGet(int FilterIndex, int Index) const { return &m_aServerlist[m_ActServerlistType].m_ppServerlist[m_ServerBrowserFilter.GetIndex(FilterIndex, Index)]->m_Info; }; const void *GetID(int FilterIndex, int Index) const { return m_ServerBrowserFilter.GetID(FilterIndex, Index); }; bool IsFavorite(const NETADDR &Addr) { return m_ServerBrowserFavorites.FindFavoriteByAddr(Addr, 0) != 0; } @@ -53,12 +54,23 @@ private: class CServerBrowserFavorites m_ServerBrowserFavorites; class CServerBrowserFilter m_ServerBrowserFilter; - class CHeap m_ServerlistHeap; - // - CServerEntry *m_aServerlistIp[256]; // ip hash list + // serverlist + int m_ActServerlistType; + class CServerlist + { + public: + class CHeap m_ServerlistHeap; - CServerEntry **m_ppServerlist; + int m_NumPlayers; + int m_NumServers; + int m_NumServerCapacity; + + CServerEntry *m_aServerlistIp[256]; // ip hash list + CServerEntry **m_ppServerlist; + + void Clear(); + } m_aServerlist[NUM_TYPES]; CServerEntry *m_pFirstReqServer; // request list CServerEntry *m_pLastReqServer; @@ -66,23 +78,18 @@ private: int m_NeedRefresh; - int m_NumServers; - int m_NumServerCapacity; - - int m_NumPlayers; - // the token is to keep server refresh separated from each other int m_CurrentLanToken; - int m_ServerlistType; + int m_RefreshFlags; int64 m_BroadcastTime; - CServerEntry *Add(const NETADDR &Addr); - CServerEntry *Find(const NETADDR &Addr); + CServerEntry *Add(int ServerlistType, const NETADDR &Addr); + CServerEntry *Find(int ServerlistType, const NETADDR &Addr); void QueueRequest(CServerEntry *pEntry); void RemoveRequest(CServerEntry *pEntry); void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const; - void SetInfo(CServerEntry *pEntry, const CServerInfo &Info); + void SetInfo(int ServerlistType, CServerEntry *pEntry, const CServerInfo &Info); }; #endif diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 71e595a9e..217d73098 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -87,7 +87,11 @@ public: QUICK_MAPNAME=4, TYPE_INTERNET = 0, - TYPE_LAN = 1, + TYPE_LAN, + NUM_TYPES, + + REFRESHFLAG_INTERNET=1, + REFRESHFLAG_LAN=2, FLAG_PASSWORD =1, FLAG_PURE =2, @@ -107,7 +111,8 @@ public: FILTER_PING=65536, }; - virtual void Refresh(int Type) = 0; + virtual void SetType(int Type) = 0; + virtual void Refresh(int RefreshFlags) = 0; virtual bool IsRefreshing() const = 0; virtual bool IsRefreshingMasters() const = 0; virtual int LoadingProgression() const = 0; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index b8cec5cd4..62e6d7ca7 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1154,7 +1154,7 @@ void CMenus::RenderMenubar(CUIRect r) if(DoButton_MenuTabTop(&s_InternetButton, Localize("Global"), m_ActivePage==PAGE_INTERNET, &Button)) { m_pClient->m_pCamera->ChangePosition(CCamera::POS_INTERNET); - ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + ServerBrowser()->SetType(IServerBrowser::TYPE_INTERNET); NewPage = PAGE_INTERNET; g_Config.m_UiBrowserPage = PAGE_INTERNET; } @@ -1165,7 +1165,7 @@ void CMenus::RenderMenubar(CUIRect r) if(DoButton_MenuTabTop(&s_LanButton, Localize("Local"), m_ActivePage==PAGE_LAN, &Button)) { m_pClient->m_pCamera->ChangePosition(CCamera::POS_LAN); - ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); + ServerBrowser()->SetType(IServerBrowser::TYPE_LAN); NewPage = PAGE_LAN; g_Config.m_UiBrowserPage = PAGE_LAN; } @@ -1598,10 +1598,8 @@ int CMenus::Render() { // refresh server browser before we are in browser menu to save time m_pClient->m_pCamera->ChangePosition(CCamera::POS_START); - if(g_Config.m_UiBrowserPage == PAGE_LAN) - ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); - else - ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + ServerBrowser()->Refresh(IServerBrowser::REFRESHFLAG_INTERNET|IServerBrowser::REFRESHFLAG_LAN); + ServerBrowser()->SetType(g_Config.m_UiBrowserPage == PAGE_LAN ? IServerBrowser::TYPE_LAN : IServerBrowser::TYPE_INTERNET); m_pClient->m_pSounds->Enqueue(CSounds::CHN_MUSIC, SOUND_MENU); s_First = false; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 1ad05d1e3..7016f0a08 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1596,9 +1596,9 @@ void CMenus::RenderServerbrowserBottomBox(CUIRect MainView) if(DoButton_Menu(&s_RefreshButton, Localize("Refresh"), 0, &Button)) { if(m_MenuPage == PAGE_INTERNET) - ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + ServerBrowser()->Refresh(IServerBrowser::REFRESHFLAG_INTERNET); else if(m_MenuPage == PAGE_LAN) - ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); + ServerBrowser()->Refresh(IServerBrowser::REFRESHFLAG_LAN); } MainView.VSplitLeft(Spacing, 0, &MainView); // little space