5246: Use std::vector instead of sorted_array, remove base/tl/sorted_array.h r=def- a=heinrich5991

This replaces all usages of `sorted_array` with `std::vector`. This requires the following general changes:

- `add_unsorted` is replaced with `push_back`.
- `add` is replaced with `push_back` and subsequent `sort` or `stable_sort` must be ensured.
  - In some cases, immediately sorting the entire list after adding an item was unavoidable. Previously items were added at the correct position, which was O(N) because all items after the inserted one had to be moved in any case.
- `sort_range` is replaced with `sort` or `stable_sort`.
- `size` returns a `size_t` instead of `int`, so to fix sign comparison warnings, casts are added where necessary or types of loop variables are changed to `size_t` where possible. For-each loops are also used where possible / where required by clang-tidy.
- `find_binary` is replaced with `std::equal_range`. This can only find items of the same type, so some wrappers, that only have the relevant fields set, need to be created for searching.

In terms of behavior, this should not change anything, except maybe `CLocalizationDatabase` for the better. As far as I understand it, at lot of the code there was not doing anything. It assumes that binary search can return a range of multiple entries, but the equality/comparison function is based on hash and context hash. This means that any item in this range will match the given hash and context hash already, so all of the following checks are redundant. I changed this to first do a lookup with the hash and context hash and if that fails do another lookup with the default context hash.

I have also already replaced `base/tl/array.h` with `std::vector`, removing all of `base/tl` except `threading.h`. I'll make a separate PR later because this caused a lot more changes especially in the editor that I first want to test and review myself.

Naming of `array`/`sorted_array`/`std::vector` variables was rather inconsistent (sometimes prefix `a` or `l` is used), so ~~I chose to not use any prefix for all new `std::vector`s~~ heinrich5991 left them as-is.

## Checklist

- [X] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)

Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
bors[bot] 2022-05-27 08:51:55 +00:00 committed by GitHub
commit 7348bf4d56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 250 additions and 314 deletions

View file

@ -1690,7 +1690,6 @@ set_src(BASE GLOB_RECURSE src/base
tl/allocator.h
tl/array.h
tl/range.h
tl/sorted_array.h
tl/threading.h
unicode/confusables.cpp
unicode/confusables.h
@ -2504,7 +2503,6 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
secure_random.cpp
serverbrowser.cpp
serverinfo.cpp
sorted_array.cpp
str.cpp
strip_path_and_extension.cpp
teehistorian.cpp

View file

@ -1,53 +0,0 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef BASE_TL_SORTED_ARRAY_H
#define BASE_TL_SORTED_ARRAY_H
#include "base/tl/algorithm.h"
#include "base/tl/array.h"
template<class T, class ALLOCATOR = allocator_default<T>>
class sorted_array : public array<T, ALLOCATOR>
{
typedef array<T, ALLOCATOR> parent;
// insert and size is not allowed
int insert(const T &item, typename parent::range r)
{
dbg_break();
return 0;
}
int set_size(int new_size)
{
dbg_break();
return 0;
}
public:
typedef plain_range_sorted<T> range;
int add(const T &item)
{
return parent::insert(item, partition_binary(all(), item));
}
int add_unsorted(const T &item)
{
return parent::add(item);
}
void sort_range()
{
if(parent::size() > 0)
sort(all());
}
/*
Function: all
Returns a sorted range that contains the whole array.
*/
range all() { return range(parent::list, parent::list + parent::num_elements); }
range all() const { return range(parent::list, parent::list + parent::num_elements); }
};
#endif // TL_FILE_SORTED_ARRAY_HPP

View file

@ -112,7 +112,7 @@ void CUuidManager::RegisterName(int ID, const char *pName)
CNameIndexed NameIndexed;
NameIndexed.m_Uuid = Name.m_Uuid;
NameIndexed.m_ID = GetIndex(ID);
m_aNamesSorted.add(NameIndexed);
m_aNamesSorted.insert(std::lower_bound(m_aNamesSorted.begin(), m_aNamesSorted.end(), NameIndexed), NameIndexed);
}
CUuid CUuidManager::GetUuid(int ID) const
@ -127,10 +127,13 @@ const char *CUuidManager::GetName(int ID) const
int CUuidManager::LookupUuid(CUuid Uuid) const
{
sorted_array<CNameIndexed>::range Pos = ::find_binary(m_aNamesSorted.all(), Uuid);
if(!Pos.empty())
CNameIndexed Needle;
Needle.m_Uuid = Uuid;
Needle.m_ID = 0;
auto Range = std::equal_range(m_aNamesSorted.begin(), m_aNamesSorted.end(), Needle);
if(std::distance(Range.first, Range.second) == 1)
{
return GetID(Pos.front().m_ID);
return GetID(Range.first->m_ID);
}
return UUID_UNKNOWN;
}

View file

@ -2,7 +2,7 @@
#define ENGINE_SHARED_UUID_MANAGER_H
#include <base/tl/array.h>
#include <base/tl/sorted_array.h>
#include <vector>
enum
{
@ -42,8 +42,7 @@ struct CNameIndexed
int m_ID;
bool operator<(const CNameIndexed &Other) const { return m_Uuid < Other.m_Uuid; }
bool operator<(const CUuid &Other) const { return m_Uuid < Other; }
bool operator==(const CUuid &Other) const { return m_Uuid == Other; }
bool operator==(const CNameIndexed &Other) const { return m_Uuid == Other.m_Uuid; }
};
class CPacker;
@ -52,7 +51,7 @@ class CUnpacker;
class CUuidManager
{
array<CName> m_aNames;
sorted_array<CNameIndexed> m_aNamesSorted;
std::vector<CNameIndexed> m_aNamesSorted;
public:
void RegisterName(int ID, const char *pName);

View file

@ -34,7 +34,7 @@ CChat::CChat()
#define CHAT_COMMAND(name, params, flags, callback, userdata, help) RegisterCommand(name, params, flags, help);
#include <game/ddracechat.h>
m_Commands.sort_range();
std::sort(m_Commands.begin(), m_Commands.end());
m_Mode = MODE_NONE;
Reset();
@ -42,7 +42,7 @@ CChat::CChat()
void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp)
{
m_Commands.add_unsorted(CCommand{pName, pParams});
m_Commands.emplace_back(pName, pParams);
}
void CChat::RebuildChat()
@ -380,7 +380,7 @@ bool CChat::OnInput(IInput::CEvent Event)
auto &Command = m_Commands[Index];
if(str_startswith(Command.pName, pCommandStart))
if(str_startswith(Command.m_pName, pCommandStart))
{
pCompletionCommand = &Command;
m_CompletionChosen = Index + SearchType * NumCommands;
@ -397,10 +397,10 @@ bool CChat::OnInput(IInput::CEvent Event)
// add the command
str_append(aBuf, "/", sizeof(aBuf));
str_append(aBuf, pCompletionCommand->pName, sizeof(aBuf));
str_append(aBuf, pCompletionCommand->m_pName, sizeof(aBuf));
// add separator
const char *pSeparator = pCompletionCommand->pParams[0] == '\0' ? "" : " ";
const char *pSeparator = pCompletionCommand->m_pParams[0] == '\0' ? "" : " ";
str_append(aBuf, pSeparator, sizeof(aBuf));
if(*pSeparator)
str_append(aBuf, pSeparator, sizeof(aBuf));
@ -408,7 +408,7 @@ bool CChat::OnInput(IInput::CEvent Event)
// add part after the name
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionCommand->pName) + 1;
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionCommand->m_pName) + 1;
m_OldChatStringLength = m_Input.GetLength();
m_Input.Set(aBuf); // TODO: Use Add instead
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength);

View file

@ -2,6 +2,8 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_CHAT_H
#define GAME_CLIENT_COMPONENTS_CHAT_H
#include <vector>
#include <engine/shared/config.h>
#include <engine/shared/ringbuffer.h>
@ -91,15 +93,21 @@ class CChat : public CComponent
struct CCommand
{
const char *pName;
const char *pParams;
const char *m_pName;
const char *m_pParams;
bool operator<(const CCommand &Other) const { return str_comp(pName, Other.pName) < 0; }
bool operator<=(const CCommand &Other) const { return str_comp(pName, Other.pName) <= 0; }
bool operator==(const CCommand &Other) const { return str_comp(pName, Other.pName) == 0; }
CCommand() {}
CCommand(const char *pName, const char *pParams) :
m_pName(pName), m_pParams(pParams)
{
}
bool operator<(const CCommand &Other) const { return str_comp(m_pName, Other.m_pName) < 0; }
bool operator<=(const CCommand &Other) const { return str_comp(m_pName, Other.m_pName) <= 0; }
bool operator==(const CCommand &Other) const { return str_comp(m_pName, Other.m_pName) == 0; }
};
sorted_array<CCommand> m_Commands;
std::vector<CCommand> m_Commands;
bool m_ReverseTAB;
struct CHistoryEntry

View file

@ -2,7 +2,6 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/logger.h>
#include <base/tl/sorted_array.h>
#include <climits>
#include <cmath>

View file

@ -1,7 +1,5 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/sorted_array.h>
#include <base/math.h>
#include <SDL.h>

View file

@ -81,15 +81,15 @@ void CCountryFlags::LoadCountryflagsIndexfile()
str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf);
}
m_aCountryFlags.add_unsorted(CountryFlag);
m_aCountryFlags.push_back(CountryFlag);
}
io_close(File);
m_aCountryFlags.sort_range();
std::sort(m_aCountryFlags.begin(), m_aCountryFlags.end());
// find index of default item
int DefaultIndex = 0, Index = 0;
for(sorted_array<CCountryFlag>::range r = m_aCountryFlags.all(); !r.empty(); r.pop_front(), ++Index)
if(r.front().m_CountryCode == -1)
size_t DefaultIndex = 0;
for(size_t Index = 0; Index < m_aCountryFlags.size(); ++Index)
if(m_aCountryFlags[Index].m_CountryCode == -1)
{
DefaultIndex = Index;
break;
@ -97,11 +97,11 @@ void CCountryFlags::LoadCountryflagsIndexfile()
// init LUT
if(DefaultIndex != 0)
for(int &CodeIndexLUT : m_CodeIndexLUT)
for(size_t &CodeIndexLUT : m_CodeIndexLUT)
CodeIndexLUT = DefaultIndex;
else
mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT));
for(int i = 0; i < m_aCountryFlags.size(); ++i)
for(size_t i = 0; i < m_aCountryFlags.size(); ++i)
m_CodeIndexLUT[maximum(0, (m_aCountryFlags[i].m_CountryCode - CODE_LB) % CODE_RANGE)] = i;
}
@ -110,13 +110,13 @@ void CCountryFlags::OnInit()
// load country flags
m_aCountryFlags.clear();
LoadCountryflagsIndexfile();
if(!m_aCountryFlags.size())
if(m_aCountryFlags.empty())
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "countryflags", "failed to load country flags. folder='countryflags/'");
CCountryFlag DummyEntry;
DummyEntry.m_CountryCode = -1;
mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString));
m_aCountryFlags.add(DummyEntry);
m_aCountryFlags.push_back(DummyEntry);
}
m_FlagsQuadContainerIndex = Graphics()->CreateQuadContainer(false);
@ -126,7 +126,7 @@ void CCountryFlags::OnInit()
Graphics()->QuadContainerUpload(m_FlagsQuadContainerIndex);
}
int CCountryFlags::Num() const
size_t CCountryFlags::Num() const
{
return m_aCountryFlags.size();
}
@ -136,9 +136,9 @@ const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCo
return GetByIndex(m_CodeIndexLUT[maximum(0, (CountryCode - CODE_LB) % CODE_RANGE)]);
}
const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const
const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(size_t Index) const
{
return &m_aCountryFlags[maximum(0, Index % m_aCountryFlags.size())];
return &m_aCountryFlags[Index % m_aCountryFlags.size()];
}
void CCountryFlags::Render(int CountryCode, const ColorRGBA *pColor, float x, float y, float w, float h)

View file

@ -2,9 +2,9 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H
#define GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H
#include <base/tl/sorted_array.h>
#include <base/vmath.h>
#include <game/client/component.h>
#include <vector>
class CCountryFlags : public CComponent
{
@ -21,9 +21,9 @@ public:
virtual int Sizeof() const override { return sizeof(*this); }
void OnInit() override;
int Num() const;
size_t Num() const;
const CCountryFlag *GetByCountryCode(int CountryCode) const;
const CCountryFlag *GetByIndex(int Index) const;
const CCountryFlag *GetByIndex(size_t Index) const;
void Render(int CountryCode, const ColorRGBA *pColor, float x, float y, float w, float h);
private:
@ -33,8 +33,8 @@ private:
CODE_UB = 999,
CODE_RANGE = CODE_UB - CODE_LB + 1,
};
sorted_array<CCountryFlag> m_aCountryFlags;
int m_CodeIndexLUT[CODE_RANGE];
std::vector<CCountryFlag> m_aCountryFlags;
size_t m_CodeIndexLUT[CODE_RANGE];
int m_FlagsQuadContainerIndex;

View file

@ -1,8 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/sorted_array.h>
#include <engine/demo.h>
#include <engine/engine.h>

View file

@ -3,8 +3,6 @@
#include <vector>
#include <base/tl/array.h>
#include <cmath>
#include <base/math.h>
@ -1873,13 +1871,13 @@ int CMenus::Render()
int OldSelected = -1;
UiDoListboxStart(&s_ScrollValue, &Box, 50.0f, Localize("Country / Region"), "", m_pClient->m_CountryFlags.Num(), 6, OldSelected, s_ScrollValue);
for(int i = 0; i < m_pClient->m_CountryFlags.Num(); ++i)
for(size_t i = 0; i < m_pClient->m_CountryFlags.Num(); ++i)
{
const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_CountryFlags.GetByIndex(i);
if(pEntry->m_CountryCode == CurSelection)
OldSelected = i;
CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i);
CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected >= 0 && (size_t)OldSelected == i);
if(Item.m_Visible)
{
CUIRect Label;

View file

@ -3,10 +3,11 @@
#ifndef GAME_CLIENT_COMPONENTS_MENUS_H
#define GAME_CLIENT_COMPONENTS_MENUS_H
#include <base/tl/sorted_array.h>
#include <base/vmath.h>
#include <chrono>
#include <vector>
#include <engine/demo.h>
#include <engine/friends.h>
#include <engine/shared/config.h>
@ -252,11 +253,11 @@ public:
};
protected:
sorted_array<SCustomEntities> m_EntitiesList;
sorted_array<SCustomGame> m_GameList;
sorted_array<SCustomEmoticon> m_EmoticonList;
sorted_array<SCustomParticle> m_ParticlesList;
sorted_array<SCustomHud> m_HudList;
std::vector<SCustomEntities> m_EntitiesList;
std::vector<SCustomGame> m_GameList;
std::vector<SCustomEmoticon> m_EmoticonList;
std::vector<SCustomParticle> m_ParticlesList;
std::vector<SCustomHud> m_HudList;
bool m_IsInit = false;
@ -428,7 +429,6 @@ protected:
}
};
//sorted_array<CDemoItem> m_lDemos;
char m_aCurrentDemoFolder[256];
char m_aCurrentDemoFile[64];
int m_DemolistSelectedIndex;
@ -448,6 +448,12 @@ protected:
const CFriendInfo *m_pFriendInfo;
int m_NumFound;
CFriendItem() {}
CFriendItem(const CFriendInfo *pFriendInfo) :
m_pFriendInfo(pFriendInfo), m_NumFound(0)
{
}
bool operator<(const CFriendItem &Other) const
{
if(m_NumFound && !Other.m_NumFound)
@ -465,7 +471,7 @@ protected:
}
};
sorted_array<CFriendItem> m_lFriends;
std::vector<CFriendItem> m_lFriends;
int m_FriendlistSelectedIndex;
void FriendlistOnUpdate();
@ -616,7 +622,7 @@ public:
// DDRace
int DoButton_CheckBox_DontCare(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
sorted_array<CDemoItem> m_lDemos;
std::vector<CDemoItem> m_lDemos;
void DemolistPopulate();
bool m_Dummy;
@ -641,7 +647,7 @@ public:
bool HasFile() const { return m_aFilename[0]; }
};
sorted_array<CGhostItem> m_lGhosts;
std::vector<CGhostItem> m_lGhosts;
std::chrono::nanoseconds m_GhostPopulateStartTime{0};

View file

@ -221,8 +221,8 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
m_SelectedIndex = -1;
// reset friend counter
for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0)
;
for(auto &Friend : m_lFriends)
Friend.m_NumFound = 0;
auto RenderBrowserIcons = [this](CUIElement::SUIElementRect &UIRect, CUIRect *pRect, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor, const char *pText, ETextAlignment TextAlign, bool SmallFont = false) {
float FontSize = 14.0f * UI()->Scale();
@ -269,13 +269,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
{
unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName);
unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan);
for(int f = 0; f < m_lFriends.size(); ++f)
for(auto &Friend : m_lFriends)
{
if(((g_Config.m_ClFriendsIgnoreClan && m_lFriends[f].m_pFriendInfo->m_aName[0]) || (ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash && !str_comp(m_lFriends[f].m_pFriendInfo->m_aClan, pItem->m_aClients[j].m_aClan))) &&
(!m_lFriends[f].m_pFriendInfo->m_aName[0] || (NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash && !str_comp(m_lFriends[f].m_pFriendInfo->m_aName, pItem->m_aClients[j].m_aName))))
if(((g_Config.m_ClFriendsIgnoreClan && Friend.m_pFriendInfo->m_aName[0]) || (ClanHash == Friend.m_pFriendInfo->m_ClanHash && !str_comp(Friend.m_pFriendInfo->m_aClan, pItem->m_aClients[j].m_aClan))) &&
(!Friend.m_pFriendInfo->m_aName[0] || (NameHash == Friend.m_pFriendInfo->m_NameHash && !str_comp(Friend.m_pFriendInfo->m_aName, pItem->m_aClients[j].m_aName))))
{
m_lFriends[f].m_NumFound++;
if(m_lFriends[f].m_pFriendInfo->m_aName[0])
Friend.m_NumFound++;
if(Friend.m_pFriendInfo->m_aName[0])
break;
}
}
@ -1250,13 +1250,8 @@ void CMenus::FriendlistOnUpdate()
{
m_lFriends.clear();
for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
{
CFriendItem Item;
Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i);
Item.m_NumFound = 0;
m_lFriends.add_unsorted(Item);
}
m_lFriends.sort_range();
m_lFriends.emplace_back(m_pClient->Friends()->GetFriend(i));
std::sort(m_lFriends.begin(), m_lFriends.end());
}
void CMenus::RenderServerbrowserFriends(CUIRect View)
@ -1286,14 +1281,14 @@ void CMenus::RenderServerbrowserFriends(CUIRect View)
// friends list(remove friend)
static float s_ScrollValue = 0;
if(m_FriendlistSelectedIndex >= m_lFriends.size())
if(m_FriendlistSelectedIndex >= (int)m_lFriends.size())
m_FriendlistSelectedIndex = m_lFriends.size() - 1;
UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
m_lFriends.sort_range();
for(int i = 0; i < m_lFriends.size(); ++i)
std::sort(m_lFriends.begin(), m_lFriends.end());
for(auto &Friend : m_lFriends)
{
CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i], false, false);
CListboxItem Item = UiDoListboxNextItem(&Friend.m_NumFound, false, false);
if(Item.m_Visible)
{
@ -1304,14 +1299,14 @@ void CMenus::RenderServerbrowserFriends(CUIRect View)
Item.m_Rect.VMargin(2.5f, &Item.m_Rect);
Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button);
UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, TEXTALIGN_LEFT);
UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, TEXTALIGN_LEFT);
UI()->DoLabelScaled(&Item.m_Rect, Friend.m_pFriendInfo->m_aName, FontSize, TEXTALIGN_LEFT);
UI()->DoLabelScaled(&Button, Friend.m_pFriendInfo->m_aClan, FontSize, TEXTALIGN_LEFT);
RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f);
RenderTools()->DrawUIRect(&OnState, Friend.m_NumFound ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.25f) : ColorRGBA(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f);
OnState.HMargin((OnState.h - FontSize) / 3, &OnState);
OnState.VMargin(5.0f, &OnState);
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound);
str_format(aBuf, sizeof(aBuf), "%i", Friend.m_NumFound);
UI()->DoLabelScaled(&OnState, aBuf, FontSize + 2, TEXTALIGN_RIGHT);
}
}

View file

@ -767,7 +767,7 @@ int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int Stora
}
Item.m_IsDir = IsDir != 0;
Item.m_StorageType = StorageType;
pSelf->m_lDemos.add_unsorted(Item);
pSelf->m_lDemos.push_back(Item);
if(tw::time_get() - pSelf->m_DemoPopulateStartTime > 500ms)
{
@ -788,7 +788,7 @@ void CMenus::DemolistPopulate()
if(g_Config.m_BrDemoFetchInfo)
FetchAllHeaders();
m_lDemos.sort_range();
std::stable_sort(m_lDemos.begin(), m_lDemos.end());
}
void CMenus::DemolistOnUpdate(bool Reset)
@ -800,11 +800,11 @@ void CMenus::DemolistOnUpdate(bool Reset)
bool Found = false;
int SelectedIndex = -1;
// search for selected index
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front())
for(auto &Item : m_lDemos)
{
SelectedIndex++;
if(str_comp(g_Config.m_UiDemoSelected, r.front().m_aName) == 0)
if(str_comp(g_Config.m_UiDemoSelected, Item.m_aName) == 0)
{
Found = true;
break;
@ -815,8 +815,8 @@ void CMenus::DemolistOnUpdate(bool Reset)
m_DemolistSelectedIndex = SelectedIndex;
}
m_DemolistSelectedIndex = Reset ? m_lDemos.size() > 0 ? 0 : -1 :
m_DemolistSelectedIndex >= m_lDemos.size() ? m_lDemos.size() - 1 : m_DemolistSelectedIndex;
m_DemolistSelectedIndex = Reset ? !m_lDemos.empty() ? 0 : -1 :
m_DemolistSelectedIndex >= (int)m_lDemos.size() ? m_lDemos.size() - 1 : m_DemolistSelectedIndex;
m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_lDemos[m_DemolistSelectedIndex].m_IsDir;
}
@ -834,11 +834,11 @@ bool CMenus::FetchHeader(CDemoItem &Item)
void CMenus::FetchAllHeaders()
{
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front())
for(auto &Item : m_lDemos)
{
FetchHeader(r.front());
FetchHeader(Item);
}
m_lDemos.sort_range();
std::stable_sort(m_lDemos.begin(), m_lDemos.end());
}
void CMenus::RenderDemoList(CUIRect MainView)
@ -1070,7 +1070,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
}
// Don't rescan in order to keep fetched headers, just resort
m_lDemos.sort_range();
std::stable_sort(m_lDemos.begin(), m_lDemos.end());
DemolistOnUpdate(false);
}
}
@ -1095,13 +1095,13 @@ void CMenus::RenderDemoList(CUIRect MainView)
CUIRect OriginalView = ListBox;
int Num = (int)(ListBox.h / s_aCols[0].m_Rect.h) + 1;
int ScrollNum = maximum(m_lDemos.size() - Num + 1, 0);
int ScrollNum = maximum<int>(m_lDemos.size() - Num + 1, 0);
ListBox.y -= s_ScrollValue * ScrollNum * s_aCols[0].m_Rect.h;
int ItemIndex = -1;
bool DoubleClicked = false;
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front())
for(auto &Item : m_lDemos)
{
ItemIndex++;
@ -1126,10 +1126,10 @@ void CMenus::RenderDemoList(CUIRect MainView)
RenderTools()->DrawUIRect(&Rect, ColorRGBA(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f);
}
if(UI()->DoButtonLogic(r.front().m_aName, Selected, &Row))
if(UI()->DoButtonLogic(Item.m_aName, Selected, &Row))
{
DoubleClicked |= ItemIndex == m_DoubleClickIndex;
str_copy(g_Config.m_UiDemoSelected, r.front().m_aName, sizeof(g_Config.m_UiDemoSelected));
str_copy(g_Config.m_UiDemoSelected, Item.m_aName, sizeof(g_Config.m_UiDemoSelected));
DemolistOnUpdate(false);
m_DoubleClickIndex = ItemIndex;
}
@ -1152,7 +1152,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
if(ID == COL_ICON)
{
DoButton_Icon(IMAGE_FILEICONS, r.front().m_IsDir ? SPRITE_FILE_FOLDER : SPRITE_FILE_DEMO1, &Button);
DoButton_Icon(IMAGE_FILEICONS, Item.m_IsDir ? SPRITE_FILE_FOLDER : SPRITE_FILE_DEMO1, &Button);
}
else if(ID == COL_DEMONAME)
{
@ -1160,27 +1160,27 @@ void CMenus::RenderDemoList(CUIRect MainView)
TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - 12.0f * UI()->Scale()) / 2.f, 12.0f * UI()->Scale(), TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = Button.w;
TextRender()->TextEx(&Cursor, r.front().m_aName, -1);
TextRender()->TextEx(&Cursor, Item.m_aName, -1);
}
else if(ID == COL_MARKERS && !r.front().m_IsDir && r.front().m_InfosLoaded)
else if(ID == COL_MARKERS && !Item.m_IsDir && Item.m_InfosLoaded)
{
char aBuf[3];
str_format(aBuf, sizeof(aBuf), "%d", r.front().NumMarkers());
str_format(aBuf, sizeof(aBuf), "%d", Item.NumMarkers());
Button.VMargin(4.0f, &Button);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT);
}
else if(ID == COL_LENGTH && !r.front().m_IsDir && r.front().m_InfosLoaded)
else if(ID == COL_LENGTH && !Item.m_IsDir && Item.m_InfosLoaded)
{
int Length = r.front().Length();
int Length = Item.Length();
char aBuf[32];
str_time((int64_t)Length * 100, TIME_HOURS, aBuf, sizeof(aBuf));
Button.VMargin(4.0f, &Button);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT);
}
else if(ID == COL_DATE && !r.front().m_IsDir)
else if(ID == COL_DATE && !Item.m_IsDir)
{
char aBuf[64];
str_timestamp_ex(r.front().m_Date, aBuf, sizeof(aBuf), FORMAT_SPACE);
str_timestamp_ex(Item.m_Date, aBuf, sizeof(aBuf), FORMAT_SPACE);
Button.VSplitRight(24.0f, &Button, 0);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT);
}

View file

@ -918,7 +918,7 @@ int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType
str_copy(Item.m_aPlayer, Info.m_aOwner, sizeof(Item.m_aPlayer));
Item.m_Time = Info.m_Time;
if(Item.m_Time > 0)
pSelf->m_lGhosts.add(Item);
pSelf->m_lGhosts.push_back(Item);
if(tw::time_get() - pSelf->m_GhostPopulateStartTime > 500ms)
{
@ -930,16 +930,15 @@ int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType
void CMenus::GhostlistPopulate()
{
CGhostItem *pOwnGhost = 0;
m_lGhosts.clear();
m_GhostPopulateStartTime = tw::time_get();
Storage()->ListDirectory(IStorage::TYPE_ALL, m_pClient->m_Ghost.GetGhostDir(), GhostlistFetchCallback, this);
std::sort(m_lGhosts.begin(), m_lGhosts.end());
for(int i = 0; i < m_lGhosts.size(); i++)
{
if(str_comp(m_lGhosts[i].m_aPlayer, Client()->PlayerName()) == 0 && (!pOwnGhost || m_lGhosts[i] < *pOwnGhost))
pOwnGhost = &m_lGhosts[i];
}
CGhostItem *pOwnGhost = 0;
for(auto &Ghost : m_lGhosts)
if(str_comp(Ghost.m_aPlayer, Client()->PlayerName()) == 0 && (!pOwnGhost || Ghost < *pOwnGhost))
pOwnGhost = &Ghost;
if(pOwnGhost)
{
@ -950,16 +949,16 @@ void CMenus::GhostlistPopulate()
CMenus::CGhostItem *CMenus::GetOwnGhost()
{
for(int i = 0; i < m_lGhosts.size(); i++)
if(m_lGhosts[i].m_Own)
return &m_lGhosts[i];
return 0;
for(auto &Ghost : m_lGhosts)
if(Ghost.m_Own)
return &Ghost;
return nullptr;
}
void CMenus::UpdateOwnGhost(CGhostItem Item)
{
int Own = -1;
for(int i = 0; i < m_lGhosts.size(); i++)
for(size_t i = 0; i < m_lGhosts.size(); i++)
if(m_lGhosts[i].m_Own)
Own = i;
@ -972,14 +971,14 @@ void CMenus::UpdateOwnGhost(CGhostItem Item)
}
Item.m_Own = true;
m_lGhosts.add(Item);
m_lGhosts.insert(std::lower_bound(m_lGhosts.begin(), m_lGhosts.end(), Item), Item);
}
void CMenus::DeleteGhostItem(int Index)
{
if(m_lGhosts[Index].HasFile())
Storage()->RemoveFile(m_lGhosts[Index].m_aFilename, IStorage::TYPE_SAVE);
m_lGhosts.remove_index(Index);
m_lGhosts.erase(m_lGhosts.begin() + Index);
}
void CMenus::RenderGhost(CUIRect MainView)
@ -1158,7 +1157,7 @@ void CMenus::RenderGhost(CUIRect MainView)
GhostlistPopulate();
}
if(s_SelectedIndex == -1 || s_SelectedIndex >= m_lGhosts.size())
if(s_SelectedIndex == -1 || s_SelectedIndex >= (int)m_lGhosts.size())
return;
CGhostItem *pGhost = &m_lGhosts[s_SelectedIndex];

View file

@ -408,13 +408,13 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
int OldSelected = -1;
UiDoListboxStart(&s_ScrollValue, &MainView, 50.0f, Localize("Country / Region"), "", m_pClient->m_CountryFlags.Num(), 6, OldSelected, s_ScrollValue);
for(int i = 0; i < m_pClient->m_CountryFlags.Num(); ++i)
for(size_t i = 0; i < m_pClient->m_CountryFlags.Num(); ++i)
{
const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_CountryFlags.GetByIndex(i);
if(pEntry->m_CountryCode == *pCountry)
OldSelected = i;
CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i, s_ListBoxUsed);
CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected >= 0 && (size_t)OldSelected == i, s_ListBoxUsed);
if(Item.m_Visible)
{
Item.m_Rect.Margin(5.0f, &Item.m_Rect);
@ -673,12 +673,12 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
// skin selector
MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(230.0f - RenderEyesBelow * 25.0f, &SkinList, &MainView);
static sorted_array<CUISkin> s_paSkinList;
static std::vector<CUISkin> s_vSkinList;
static int s_SkinCount = 0;
static float s_ScrollValue = 0.0f;
if(s_InitSkinlist || m_pClient->m_Skins.Num() != s_SkinCount)
{
s_paSkinList.clear();
s_vSkinList.clear();
for(int i = 0; i < m_pClient->m_Skins.Num(); ++i)
{
const CSkin *s = m_pClient->m_Skins.Get(i);
@ -698,22 +698,23 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
if(s == 0)
continue;
s_paSkinList.add(CUISkin(s));
s_vSkinList.emplace_back(s);
}
std::sort(s_vSkinList.begin(), s_vSkinList.end());
s_InitSkinlist = false;
s_SkinCount = m_pClient->m_Skins.Num();
}
int OldSelected = -1;
UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_paSkinList.size(), 4, OldSelected, s_ScrollValue);
for(int i = 0; i < s_paSkinList.size(); ++i)
UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_vSkinList.size(), 4, OldSelected, s_ScrollValue);
for(size_t i = 0; i < s_vSkinList.size(); ++i)
{
const CSkin *s = s_paSkinList[i].m_pSkin;
const CSkin *s = s_vSkinList[i].m_pSkin;
if(str_comp(s->m_aName, pSkinName) == 0)
OldSelected = i;
CListboxItem Item = UiDoListboxNextItem(s_paSkinList[i].m_pSkin, OldSelected == i);
CListboxItem Item = UiDoListboxNextItem(s, OldSelected >= 0 && (size_t)OldSelected == i);
if(Item.m_Visible)
{
CTeeRenderInfo Info = OwnSkinInfo;
@ -728,10 +729,9 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
RenderTools()->RenderTee(pIdleState, &Info, Emote, vec2(1.0f, 0.0f), TeeRenderPos);
Item.m_Rect.VSplitLeft(60.0f, 0, &Item.m_Rect);
str_format(aBuf, sizeof(aBuf), "%s", s->m_aName);
SLabelProperties Props;
Props.m_MaxWidth = Item.m_Rect.w;
UI()->DoLabelScaled(&Item.m_Rect, aBuf, 12.0f, TEXTALIGN_LEFT, Props);
UI()->DoLabelScaled(&Item.m_Rect, s->m_aName, 12.0f, TEXTALIGN_LEFT, Props);
if(g_Config.m_Debug)
{
ColorRGBA BloodColor = *UseCustomColor ? color_cast<ColorRGBA>(ColorHSLA(*ColorBody)) : s->m_BloodColor;
@ -748,7 +748,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
if(OldSelected != NewSelected)
{
mem_copy(pSkinName, s_paSkinList[NewSelected].m_pSkin->m_aName, sizeof(g_Config.m_ClPlayerSkin));
mem_copy(pSkinName, s_vSkinList[NewSelected].m_pSkin->m_aName, sizeof(g_Config.m_ClPlayerSkin));
SetNeedSendInfo();
}
@ -1704,7 +1704,7 @@ public:
bool operator<(const CLanguage &Other) const { return m_Name < Other.m_Name; }
};
void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<CLanguage> *pLanguages)
void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, std::vector<CLanguage> &Languages)
{
IOHANDLE File = pStorage->OpenFile("languages/index.txt", IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL);
if(!File)
@ -1759,7 +1759,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
char aFileName[IO_MAX_PATH_LENGTH];
str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin);
pLanguages->add(CLanguage(aReplacement, aFileName, str_toint(pLine + 3)));
Languages.emplace_back(aReplacement, aFileName, str_toint(pLine + 3));
}
io_close(File);
}
@ -1768,14 +1768,15 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
{
static int s_LanguageList = 0;
static int s_SelectedLanguage = 0;
static sorted_array<CLanguage> s_Languages;
static std::vector<CLanguage> s_Languages;
static float s_ScrollValue = 0;
if(s_Languages.size() == 0)
if(s_Languages.empty())
{
s_Languages.add(CLanguage("English", "", 826));
LoadLanguageIndexfile(Storage(), Console(), &s_Languages);
for(int i = 0; i < s_Languages.size(); i++)
s_Languages.emplace_back("English", "", 826);
LoadLanguageIndexfile(Storage(), Console(), s_Languages);
std::sort(s_Languages.begin(), s_Languages.end());
for(size_t i = 0; i < s_Languages.size(); i++)
if(str_comp(s_Languages[i].m_FileName.c_str(), g_Config.m_ClLanguagefile) == 0)
{
s_SelectedLanguage = i;
@ -1787,10 +1788,9 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
UiDoListboxStart(&s_LanguageList, &MainView, 24.0f, Localize("Language"), "", s_Languages.size(), 1, s_SelectedLanguage, s_ScrollValue);
for(sorted_array<CLanguage>::range r = s_Languages.all(); !r.empty(); r.pop_front())
for(auto &Language : s_Languages)
{
CListboxItem Item = UiDoListboxNextItem(&r.front());
CListboxItem Item = UiDoListboxNextItem(&Language.m_Name);
if(Item.m_Visible)
{
CUIRect Rect;
@ -1798,9 +1798,9 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
Rect.VMargin(6.0f, &Rect);
Rect.HMargin(3.0f, &Rect);
ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f);
m_pClient->m_CountryFlags.Render(r.front().m_CountryCode, &Color, Rect.x, Rect.y, Rect.w, Rect.h);
m_pClient->m_CountryFlags.Render(Language.m_CountryCode, &Color, Rect.x, Rect.y, Rect.w, Rect.h);
Item.m_Rect.HSplitTop(2.0f, 0, &Item.m_Rect);
UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name.c_str(), 16.0f, TEXTALIGN_LEFT);
UI()->DoLabelScaled(&Item.m_Rect, Language.m_Name.c_str(), 16.0f, TEXTALIGN_LEFT);
}
}

View file

@ -101,7 +101,7 @@ int CMenus::EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser)
SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, pName, sizeof(EntitiesItem.m_aName));
CMenus::LoadEntities(&EntitiesItem, pUser);
pThis->m_EntitiesList.add(EntitiesItem);
pThis->m_EntitiesList.push_back(EntitiesItem);
}
else
{
@ -116,7 +116,7 @@ int CMenus::EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser)
SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, aName, sizeof(EntitiesItem.m_aName));
CMenus::LoadEntities(&EntitiesItem, pUser);
pThis->m_EntitiesList.add(EntitiesItem);
pThis->m_EntitiesList.push_back(EntitiesItem);
}
}
@ -162,7 +162,7 @@ static void LoadAsset(TName *pAssetItem, const char *pAssetName, IGraphics *pGra
}
template<typename TName>
static int AssetScan(const char *pName, int IsDir, int DirType, sorted_array<TName> &AssetList, const char *pAssetName, IGraphics *pGraphics, void *pUser)
static int AssetScan(const char *pName, int IsDir, int DirType, std::vector<TName> &AssetList, const char *pAssetName, IGraphics *pGraphics, void *pUser)
{
auto *pRealUser = (SMenuAssetScanUser *)pUser;
if(IsDir)
@ -177,7 +177,7 @@ static int AssetScan(const char *pName, int IsDir, int DirType, sorted_array<TNa
TName AssetItem;
str_copy(AssetItem.m_aName, pName, sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, pUser);
AssetList.add(AssetItem);
AssetList.push_back(AssetItem);
}
else
{
@ -192,7 +192,7 @@ static int AssetScan(const char *pName, int IsDir, int DirType, sorted_array<TNa
TName AssetItem;
str_copy(AssetItem.m_aName, aName, sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, pUser);
AssetList.add(AssetItem);
AssetList.push_back(AssetItem);
}
}
@ -233,18 +233,18 @@ int CMenus::HudScan(const char *pName, int IsDir, int DirType, void *pUser)
return AssetScan(pName, IsDir, DirType, pThis->m_HudList, "hud", pGraphics, pUser);
}
static sorted_array<const CMenus::SCustomEntities *> s_SearchEntitiesList;
static sorted_array<const CMenus::SCustomGame *> s_SearchGamesList;
static sorted_array<const CMenus::SCustomEmoticon *> s_SearchEmoticonsList;
static sorted_array<const CMenus::SCustomParticle *> s_SearchParticlesList;
static sorted_array<const CMenus::SCustomHud *> s_SearchHudList;
static std::vector<const CMenus::SCustomEntities *> s_SearchEntitiesList;
static std::vector<const CMenus::SCustomGame *> s_SearchGamesList;
static std::vector<const CMenus::SCustomEmoticon *> s_SearchEmoticonsList;
static std::vector<const CMenus::SCustomParticle *> s_SearchParticlesList;
static std::vector<const CMenus::SCustomHud *> s_SearchHudList;
static const int NumberOfAssetsTabs = 5;
static bool s_InitCustomList[NumberOfAssetsTabs] = {
true,
};
static int s_CustomListSize[NumberOfAssetsTabs] = {
static size_t s_CustomListSize[NumberOfAssetsTabs] = {
0,
};
@ -252,7 +252,7 @@ static char s_aFilterString[NumberOfAssetsTabs][50];
static int s_CurCustomTab = ASSETS_TAB_ENTITIES;
static const CMenus::SCustomItem *GetCustomItem(int CurTab, int Index)
static const CMenus::SCustomItem *GetCustomItem(int CurTab, size_t Index)
{
if(CurTab == ASSETS_TAB_ENTITIES)
return s_SearchEntitiesList[Index];
@ -269,9 +269,9 @@ static const CMenus::SCustomItem *GetCustomItem(int CurTab, int Index)
}
template<typename TName>
void ClearAssetList(sorted_array<TName> &List, IGraphics *pGraphics)
void ClearAssetList(std::vector<TName> &List, IGraphics *pGraphics)
{
for(int i = 0; i < List.size(); ++i)
for(size_t i = 0; i < List.size(); ++i)
{
if(List[i].m_RenderTexture.IsValid())
pGraphics->UnloadTexture(&(List[i].m_RenderTexture));
@ -284,9 +284,9 @@ void CMenus::ClearCustomItems(int CurTab)
{
if(CurTab == ASSETS_TAB_ENTITIES)
{
for(int i = 0; i < m_EntitiesList.size(); ++i)
for(auto &Entity : m_EntitiesList)
{
for(auto &Image : m_EntitiesList[i].m_aImages)
for(auto &Image : Entity.m_aImages)
{
if(Image.m_Texture.IsValid())
Graphics()->UnloadTexture(&Image.m_Texture);
@ -330,24 +330,25 @@ void CMenus::ClearCustomItems(int CurTab)
}
template<typename TName, typename TCaller>
void InitAssetList(sorted_array<TName> &AssetList, const char *pAssetPath, const char *pAssetName, FS_LISTDIR_CALLBACK pfnCallback, IGraphics *pGraphics, IStorage *pStorage, TCaller Caller)
void InitAssetList(std::vector<TName> &AssetList, const char *pAssetPath, const char *pAssetName, FS_LISTDIR_CALLBACK pfnCallback, IGraphics *pGraphics, IStorage *pStorage, TCaller Caller)
{
if(AssetList.size() == 0)
if(AssetList.empty())
{
TName AssetItem;
str_copy(AssetItem.m_aName, "default", sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, Caller);
AssetList.add(AssetItem);
AssetList.push_back(AssetItem);
// load assets
pStorage->ListDirectory(IStorage::TYPE_ALL, pAssetPath, pfnCallback, Caller);
std::sort(AssetList.begin(), AssetList.end());
}
if(AssetList.size() != s_CustomListSize[s_CurCustomTab])
s_InitCustomList[s_CurCustomTab] = true;
}
template<typename TName>
int InitSearchList(sorted_array<const TName *> &SearchList, sorted_array<TName> &AssetList)
int InitSearchList(std::vector<const TName *> &SearchList, std::vector<TName> &AssetList)
{
SearchList.clear();
int ListSize = AssetList.size();
@ -359,7 +360,7 @@ int InitSearchList(sorted_array<const TName *> &SearchList, sorted_array<TName>
if(s_aFilterString[s_CurCustomTab][0] != '\0' && !str_utf8_find_nocase(s->m_aName, s_aFilterString[s_CurCustomTab]))
continue;
SearchList.add_unsorted(s);
SearchList.push_back(s);
}
return AssetList.size();
}
@ -397,15 +398,16 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
};
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
{
if(m_EntitiesList.size() == 0)
if(m_EntitiesList.empty())
{
SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, "default", sizeof(EntitiesItem.m_aName));
LoadEntities(&EntitiesItem, &User);
m_EntitiesList.add(EntitiesItem);
m_EntitiesList.push_back(EntitiesItem);
// load entities
Storage()->ListDirectory(IStorage::TYPE_ALL, "assets/entities", EntitiesScan, &User);
std::sort(m_EntitiesList.begin(), m_EntitiesList.end());
}
if(m_EntitiesList.size() != s_CustomListSize[s_CurCustomTab])
s_InitCustomList[s_CurCustomTab] = true;
@ -447,7 +449,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
if(s_aFilterString[s_CurCustomTab][0] != '\0' && !str_utf8_find_nocase(s->m_aName, s_aFilterString[s_CurCustomTab]))
continue;
s_SearchEntitiesList.add_unsorted(s);
s_SearchEntitiesList.push_back(s);
}
}
else if(s_CurCustomTab == ASSETS_TAB_GAME)
@ -475,7 +477,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
float TextureWidth = 150;
float TextureHeight = 150;
int SearchListSize = 0;
size_t SearchListSize = 0;
if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
{
@ -501,7 +503,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
}
UiDoListboxStart(&s_InitCustomList[s_CurCustomTab], &CustomList, TextureHeight + 15.0f + 10.0f + Margin, "", "", SearchListSize, CustomList.w / (Margin + TextureWidth), OldSelected, s_ScrollValue, true);
for(int i = 0; i < SearchListSize; ++i)
for(size_t i = 0; i < SearchListSize; ++i)
{
const SCustomItem *s = GetCustomItem(s_CurCustomTab, i);
if(s == NULL)
@ -533,7 +535,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
OldSelected = i;
}
CListboxItem Item = UiDoListboxNextItem(s, OldSelected == i);
CListboxItem Item = UiDoListboxNextItem(s, OldSelected >= 0 && (size_t)OldSelected == i);
CUIRect ItemRect = Item.m_Rect;
ItemRect.Margin(Margin / 2, &ItemRect);
if(Item.m_Visible)

View file

@ -1,8 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/sorted_array.h>
#include <engine/demo.h>
#include <engine/engine.h>
#include <engine/graphics.h>

View file

@ -295,7 +295,7 @@ int CSkins::LoadSkin(const char *pName, CImageInfo &Info)
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf);
}
m_aSkins.add(Skin);
m_aSkins.insert(std::lower_bound(m_aSkins.begin(), m_aSkins.end(), Skin), Skin);
return 0;
}
@ -324,24 +324,24 @@ void CSkins::OnInit()
void CSkins::Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc)
{
for(int i = 0; i < m_aSkins.size(); ++i)
for(auto &Skin : m_aSkins)
{
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_Body);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_BodyOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_Feet);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_FeetOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_Hands);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_HandsOutline);
for(auto &Eye : m_aSkins[i].m_OriginalSkin.m_Eyes)
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Body);
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_BodyOutline);
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Feet);
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_FeetOutline);
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Hands);
Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_HandsOutline);
for(auto &Eye : Skin.m_OriginalSkin.m_Eyes)
Graphics()->UnloadTexture(&Eye);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Body);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_BodyOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Feet);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_FeetOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Hands);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_HandsOutline);
for(auto &Eye : m_aSkins[i].m_ColorableSkin.m_Eyes)
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Body);
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_BodyOutline);
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Feet);
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_FeetOutline);
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Hands);
Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_HandsOutline);
for(auto &Eye : Skin.m_ColorableSkin.m_Eyes)
Graphics()->UnloadTexture(&Eye);
}
@ -351,14 +351,14 @@ void CSkins::Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc)
SkinScanUser.m_pThis = this;
SkinScanUser.m_SkinLoadedFunc = SkinLoadedFunc;
Storage()->ListDirectory(IStorage::TYPE_ALL, "skins", SkinScan, &SkinScanUser);
if(!m_aSkins.size())
if(m_aSkins.empty())
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load skins. folder='skins/'");
CSkin DummySkin;
DummySkin.m_IsVanilla = true;
str_copy(DummySkin.m_aName, "dummy", sizeof(DummySkin.m_aName));
DummySkin.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f);
m_aSkins.add(DummySkin);
m_aSkins.push_back(DummySkin);
}
}
@ -402,9 +402,12 @@ int CSkins::Find(const char *pName)
int CSkins::FindImpl(const char *pName)
{
auto r = ::find_binary(m_aSkins.all(), pName);
if(!r.empty())
return &r.front() - m_aSkins.base_ptr();
CSkin Needle;
mem_zero(&Needle, sizeof(Needle));
str_copy(Needle.m_aName, pName, sizeof(Needle.m_aName));
auto Range = std::equal_range(m_aSkins.begin(), m_aSkins.end(), Needle);
if(std::distance(Range.first, Range.second) == 1)
return Range.first - m_aSkins.begin();
if(str_comp(pName, "default") == 0)
return -1;
@ -415,20 +418,23 @@ int CSkins::FindImpl(const char *pName)
if(str_find(pName, "/") != 0)
return -1;
auto d = ::find_binary(m_aDownloadSkins.all(), pName);
if(!d.empty())
CDownloadSkin DownloadNeedle;
mem_zero(&DownloadNeedle, sizeof(DownloadNeedle));
str_copy(DownloadNeedle.m_aName, pName, sizeof(DownloadNeedle.m_aName));
const auto &[RangeBegin, RangeEnd] = std::equal_range(m_aDownloadSkins.begin(), m_aDownloadSkins.end(), DownloadNeedle);
if(std::distance(RangeBegin, RangeEnd) == 1)
{
if(d.front().m_pTask && d.front().m_pTask->State() == HTTP_DONE)
if(RangeBegin->m_pTask && RangeBegin->m_pTask->State() == HTTP_DONE)
{
char aPath[IO_MAX_PATH_LENGTH];
str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", d.front().m_aName);
Storage()->RenameFile(d.front().m_aPath, aPath, IStorage::TYPE_SAVE);
LoadSkin(d.front().m_aName, d.front().m_pTask->m_Info);
d.front().m_pTask = nullptr;
str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", RangeBegin->m_aName);
Storage()->RenameFile(RangeBegin->m_aPath, aPath, IStorage::TYPE_SAVE);
LoadSkin(RangeBegin->m_aName, RangeBegin->m_pTask->m_Info);
RangeBegin->m_pTask = nullptr;
}
if(d.front().m_pTask && (d.front().m_pTask->State() == HTTP_ERROR || d.front().m_pTask->State() == HTTP_ABORTED))
if(RangeBegin->m_pTask && (RangeBegin->m_pTask->State() == HTTP_ERROR || RangeBegin->m_pTask->State() == HTTP_ABORTED))
{
d.front().m_pTask = nullptr;
RangeBegin->m_pTask = nullptr;
}
return -1;
}
@ -444,6 +450,6 @@ int CSkins::FindImpl(const char *pName)
str_format(Skin.m_aPath, sizeof(Skin.m_aPath), "downloadedskins/%s", IStorage::FormatTmpPath(aBuf, sizeof(aBuf), pName));
Skin.m_pTask = std::make_shared<CGetPngFile>(this, aUrl, Storage(), Skin.m_aPath);
m_pClient->Engine()->AddJob(Skin.m_pTask);
m_aDownloadSkins.add(Skin);
m_aDownloadSkins.insert(std::lower_bound(m_aDownloadSkins.begin(), m_aDownloadSkins.end(), Skin), Skin);
return -1;
}

View file

@ -3,11 +3,11 @@
#ifndef GAME_CLIENT_COMPONENTS_SKINS_H
#define GAME_CLIENT_COMPONENTS_SKINS_H
#include <base/color.h>
#include <base/tl/sorted_array.h>
#include <base/vmath.h>
#include <engine/shared/http.h>
#include <game/client/component.h>
#include <game/client/skin.h>
#include <vector>
class CSkins : public CComponent
{
@ -46,8 +46,8 @@ public:
int Find(const char *pName);
private:
sorted_array<CSkin> m_aSkins;
sorted_array<CDownloadSkin> m_aDownloadSkins;
std::vector<CSkin> m_aSkins;
std::vector<CDownloadSkin> m_aDownloadSkins;
char m_EventSkinPrefix[24];
bool LoadSkinPNG(CImageInfo &Info, const char *pName, const char *pPath, int DirType);

View file

@ -1,8 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/array.h>
#include "sounds.h"
#include <engine/engine.h>
#include <engine/shared/config.h>

View file

@ -1,8 +1,6 @@
#ifndef GAME_CLIENT_SKIN_H
#define GAME_CLIENT_SKIN_H
#include <base/color.h>
#include <base/tl/sorted_array.h>
#include <base/vmath.h>
#include <engine/graphics.h>
#include <limits>
@ -132,9 +130,7 @@ struct CSkin
SSkinMetrics m_Metrics;
bool operator<(const CSkin &Other) const { return str_comp(m_aName, Other.m_aName) < 0; }
bool operator<(const char *pOther) const { return str_comp(m_aName, pOther) < 0; }
bool operator==(const char *pOther) const { return !str_comp(m_aName, pOther); }
bool operator==(const CSkin &Other) const { return !str_comp(m_aName, Other.m_aName); }
};
#endif

View file

@ -4331,7 +4331,7 @@ static int EditorListdirCallback(const char *pName, int IsDir, int StorageType,
Item.m_IsDir = IsDir != 0;
Item.m_IsLink = false;
Item.m_StorageType = StorageType;
pEditor->m_FileList.add(Item);
pEditor->m_FileList.push_back(Item);
return 0;
}
@ -4462,7 +4462,7 @@ void CEditor::RenderFileDialog()
m_FileDialogScrollValue = UIEx()->DoScrollbarV(&m_FileDialogScrollValue, &Scroll, m_FileDialogScrollValue);
int ScrollNum = 0;
for(int i = 0; i < m_FileList.size(); i++)
for(size_t i = 0; i < m_FileList.size(); i++)
{
m_FileList[i].m_IsVisible = false;
if(!m_aFileDialogSearchText[0] || str_utf8_find_nocase(m_FileList[i].m_aName, m_aFileDialogSearchText))
@ -4498,7 +4498,7 @@ void CEditor::RenderFileDialog()
{
if(Input()->GetEvent(i).m_Key == KEY_DOWN)
{
for(NewIndex = m_FilesSelectedIndex + 1; NewIndex < m_FileList.size(); NewIndex++)
for(NewIndex = m_FilesSelectedIndex + 1; NewIndex < (int)m_FileList.size(); NewIndex++)
{
if(m_FileList[NewIndex].m_IsVisible)
break;
@ -4513,7 +4513,7 @@ void CEditor::RenderFileDialog()
}
}
}
if(NewIndex > -1 && NewIndex < m_FileList.size())
if(NewIndex > -1 && NewIndex < (int)m_FileList.size())
{
//scroll
float IndexY = View.y - m_FileDialogScrollValue * ScrollNum * 17.0f + NewIndex * 17.0f;
@ -4710,10 +4710,11 @@ void CEditor::FilelistPopulate(int StorageType)
Item.m_IsDir = true;
Item.m_IsLink = true;
Item.m_StorageType = IStorage::TYPE_SAVE;
m_FileList.add(Item);
m_FileList.push_back(Item);
}
Storage()->ListDirectory(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
m_FilesSelectedIndex = m_FileList.size() ? 0 : -1;
std::sort(m_FileList.begin(), m_FileList.end());
m_FilesSelectedIndex = m_FileList.empty() ? -1 : 0;
m_PreviewImageIsLoaded = false;
m_FileDialogActivate = false;

View file

@ -12,7 +12,6 @@
#include <base/tl/algorithm.h>
#include <base/tl/array.h>
#include <base/tl/sorted_array.h>
#include <game/client/render.h>
#include <game/client/ui.h>
@ -884,7 +883,7 @@ public:
bool operator<(const CFilelistItem &Other) const { return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : str_comp_nocase(m_aFilename, Other.m_aFilename) < 0; }
};
sorted_array<CFilelistItem> m_FileList;
std::vector<CFilelistItem> m_FileList;
int m_FilesStartAt;
int m_FilesCur;
int m_FilesStopAt;

View file

@ -1,8 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/array.h>
#include "editor.h"
#include <engine/client.h>
#include <engine/console.h>

View file

@ -2,7 +2,6 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "localization.h"
#include <base/tl/algorithm.h>
#include <engine/console.h>
#include <engine/shared/linereader.h>
@ -38,11 +37,7 @@ CLocalizationDatabase::CLocalizationDatabase()
void CLocalizationDatabase::AddString(const char *pOrgStr, const char *pNewStr, const char *pContext)
{
CString s;
s.m_Hash = str_quickhash(pOrgStr);
s.m_ContextHash = str_quickhash(pContext);
s.m_pReplacement = m_StringsHeap.StoreString(*pNewStr ? pNewStr : pOrgStr);
m_Strings.add(s);
m_Strings.emplace_back(str_quickhash(pOrgStr), str_quickhash(pContext), m_StringsHeap.StoreString(*pNewStr ? pNewStr : pOrgStr));
}
bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, IConsole *pConsole)
@ -118,6 +113,7 @@ bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, ICon
AddString(aOrigin, pReplacement, aContext);
}
io_close(IoHandle);
std::sort(m_Strings.begin(), m_Strings.end());
m_CurrentVersion = ++m_VersionCounter;
return true;
@ -129,22 +125,21 @@ const char *CLocalizationDatabase::FindString(unsigned Hash, unsigned ContextHas
String.m_Hash = Hash;
String.m_ContextHash = ContextHash;
String.m_pReplacement = 0x0;
sorted_array<CString>::range r = ::find_binary(m_Strings.all(), String);
if(r.empty())
return 0;
auto Range1 = std::equal_range(m_Strings.begin(), m_Strings.end(), String);
if(std::distance(Range1.first, Range1.second) == 1)
return Range1.first->m_pReplacement;
unsigned DefaultHash = str_quickhash("");
unsigned DefaultIndex = 0;
for(unsigned i = 0; i < r.size() && r.index(i).m_Hash == Hash; ++i)
const unsigned DefaultHash = str_quickhash("");
if(ContextHash != DefaultHash)
{
const CString &rStr = r.index(i);
if(rStr.m_ContextHash == ContextHash)
return rStr.m_pReplacement;
else if(rStr.m_ContextHash == DefaultHash)
DefaultIndex = i;
// Do another lookup with the default context hash
String.m_ContextHash = DefaultHash;
auto Range2 = std::equal_range(m_Strings.begin(), m_Strings.end(), String);
if(std::distance(Range2.first, Range2.second) == 1)
return Range2.first->m_pReplacement;
}
return r.index(DefaultIndex).m_pReplacement;
return nullptr;
}
CLocalizationDatabase g_Localization;

View file

@ -2,9 +2,10 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_LOCALIZATION_H
#define GAME_LOCALIZATION_H
#include <base/tl/sorted_array.h>
#include <base/system.h> // GNUC_ATTRIBUTE
#include <engine/shared/memheap.h>
#include <vector>
class CLocalizationDatabase
{
@ -15,12 +16,18 @@ class CLocalizationDatabase
unsigned m_ContextHash;
const char *m_pReplacement;
CString() {}
CString(unsigned Hash, unsigned ContextHash, const char *pReplacement) :
m_Hash(Hash), m_ContextHash(ContextHash), m_pReplacement(pReplacement)
{
}
bool operator<(const CString &Other) const { return m_Hash < Other.m_Hash || (m_Hash == Other.m_Hash && m_ContextHash < Other.m_ContextHash); }
bool operator<=(const CString &Other) const { return m_Hash < Other.m_Hash || (m_Hash == Other.m_Hash && m_ContextHash <= Other.m_ContextHash); }
bool operator==(const CString &Other) const { return m_Hash == Other.m_Hash && m_ContextHash == Other.m_ContextHash; }
};
sorted_array<CString> m_Strings;
std::vector<CString> m_Strings;
CHeap m_StringsHeap;
int m_VersionCounter;
int m_CurrentVersion;

View file

@ -1,7 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/sorted_array.h>
#include <iostream>
#include <vector>
#include "base/system.h"
#include "gamecontext.h"
@ -3080,18 +3080,19 @@ void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;
sorted_array<CMapNameItem> MapList;
std::vector<CMapNameItem> MapList;
pSelf->Storage()->ListDirectory(IStorage::TYPE_ALL, "maps", MapScan, &MapList);
std::sort(MapList.begin(), MapList.end());
for(int i = 0; i < MapList.size(); i++)
for(auto &Item : MapList)
{
char aDescription[64];
str_format(aDescription, sizeof(aDescription), "Map: %s", MapList[i].m_aName);
str_format(aDescription, sizeof(aDescription), "Map: %s", Item.m_aName);
char aCommand[IO_MAX_PATH_LENGTH * 2 + 10];
char aMapEscaped[IO_MAX_PATH_LENGTH * 2];
char *pDst = aMapEscaped;
str_escape(&pDst, MapList[i].m_aName, aMapEscaped + sizeof(aMapEscaped));
str_escape(&pDst, Item.m_aName, aMapEscaped + sizeof(aMapEscaped));
str_format(aCommand, sizeof(aCommand), "change_map \"%s\"", aMapEscaped);
pSelf->AddVote(aDescription, aCommand);
@ -3102,15 +3103,12 @@ void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData)
int CGameContext::MapScan(const char *pName, int IsDir, int DirType, void *pUserData)
{
sorted_array<CMapNameItem> *pMapList = (sorted_array<CMapNameItem> *)pUserData;
if(IsDir || !str_endswith(pName, ".map"))
return 0;
CMapNameItem Item;
int Length = str_length(pName);
str_truncate(Item.m_aName, sizeof(Item.m_aName), pName, Length - 4);
pMapList->add(Item);
str_truncate(Item.m_aName, sizeof(Item.m_aName), pName, str_length(pName) - str_length(".map"));
static_cast<std::vector<CMapNameItem> *>(pUserData)->push_back(Item);
return 0;
}

View file

@ -1,9 +0,0 @@
#include <gtest/gtest.h>
#include <base/tl/sorted_array.h>
TEST(SortedArray, SortEmptyRange)
{
sorted_array<int> x;
x.sort_range();
}

View file

@ -1,6 +1,5 @@
#include <base/logger.h>
#include <base/system.h>
#include <base/tl/array.h>
#include <engine/shared/datafile.h>
#include <engine/storage.h>
#include <game/mapitems.h>