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/allocator.h
tl/array.h tl/array.h
tl/range.h tl/range.h
tl/sorted_array.h
tl/threading.h tl/threading.h
unicode/confusables.cpp unicode/confusables.cpp
unicode/confusables.h unicode/confusables.h
@ -2504,7 +2503,6 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
secure_random.cpp secure_random.cpp
serverbrowser.cpp serverbrowser.cpp
serverinfo.cpp serverinfo.cpp
sorted_array.cpp
str.cpp str.cpp
strip_path_and_extension.cpp strip_path_and_extension.cpp
teehistorian.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; CNameIndexed NameIndexed;
NameIndexed.m_Uuid = Name.m_Uuid; NameIndexed.m_Uuid = Name.m_Uuid;
NameIndexed.m_ID = GetIndex(ID); 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 CUuid CUuidManager::GetUuid(int ID) const
@ -127,10 +127,13 @@ const char *CUuidManager::GetName(int ID) const
int CUuidManager::LookupUuid(CUuid Uuid) const int CUuidManager::LookupUuid(CUuid Uuid) const
{ {
sorted_array<CNameIndexed>::range Pos = ::find_binary(m_aNamesSorted.all(), Uuid); CNameIndexed Needle;
if(!Pos.empty()) 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; return UUID_UNKNOWN;
} }

View file

@ -2,7 +2,7 @@
#define ENGINE_SHARED_UUID_MANAGER_H #define ENGINE_SHARED_UUID_MANAGER_H
#include <base/tl/array.h> #include <base/tl/array.h>
#include <base/tl/sorted_array.h> #include <vector>
enum enum
{ {
@ -42,8 +42,7 @@ struct CNameIndexed
int m_ID; int m_ID;
bool operator<(const CNameIndexed &Other) const { return m_Uuid < Other.m_Uuid; } 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 CNameIndexed &Other) const { return m_Uuid == Other.m_Uuid; }
bool operator==(const CUuid &Other) const { return m_Uuid == Other; }
}; };
class CPacker; class CPacker;
@ -52,7 +51,7 @@ class CUnpacker;
class CUuidManager class CUuidManager
{ {
array<CName> m_aNames; array<CName> m_aNames;
sorted_array<CNameIndexed> m_aNamesSorted; std::vector<CNameIndexed> m_aNamesSorted;
public: public:
void RegisterName(int ID, const char *pName); 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); #define CHAT_COMMAND(name, params, flags, callback, userdata, help) RegisterCommand(name, params, flags, help);
#include <game/ddracechat.h> #include <game/ddracechat.h>
m_Commands.sort_range(); std::sort(m_Commands.begin(), m_Commands.end());
m_Mode = MODE_NONE; m_Mode = MODE_NONE;
Reset(); Reset();
@ -42,7 +42,7 @@ CChat::CChat()
void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp) 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() void CChat::RebuildChat()
@ -380,7 +380,7 @@ bool CChat::OnInput(IInput::CEvent Event)
auto &Command = m_Commands[Index]; auto &Command = m_Commands[Index];
if(str_startswith(Command.pName, pCommandStart)) if(str_startswith(Command.m_pName, pCommandStart))
{ {
pCompletionCommand = &Command; pCompletionCommand = &Command;
m_CompletionChosen = Index + SearchType * NumCommands; m_CompletionChosen = Index + SearchType * NumCommands;
@ -397,10 +397,10 @@ bool CChat::OnInput(IInput::CEvent Event)
// add the command // add the command
str_append(aBuf, "/", sizeof(aBuf)); str_append(aBuf, "/", sizeof(aBuf));
str_append(aBuf, pCompletionCommand->pName, sizeof(aBuf)); str_append(aBuf, pCompletionCommand->m_pName, sizeof(aBuf));
// add separator // add separator
const char *pSeparator = pCompletionCommand->pParams[0] == '\0' ? "" : " "; const char *pSeparator = pCompletionCommand->m_pParams[0] == '\0' ? "" : " ";
str_append(aBuf, pSeparator, sizeof(aBuf)); str_append(aBuf, pSeparator, sizeof(aBuf));
if(*pSeparator) if(*pSeparator)
str_append(aBuf, pSeparator, sizeof(aBuf)); str_append(aBuf, pSeparator, sizeof(aBuf));
@ -408,7 +408,7 @@ bool CChat::OnInput(IInput::CEvent Event)
// add part after the name // add part after the name
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf)); 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_OldChatStringLength = m_Input.GetLength();
m_Input.Set(aBuf); // TODO: Use Add instead m_Input.Set(aBuf); // TODO: Use Add instead
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength); 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. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_CHAT_H #ifndef GAME_CLIENT_COMPONENTS_CHAT_H
#define GAME_CLIENT_COMPONENTS_CHAT_H #define GAME_CLIENT_COMPONENTS_CHAT_H
#include <vector>
#include <engine/shared/config.h> #include <engine/shared/config.h>
#include <engine/shared/ringbuffer.h> #include <engine/shared/ringbuffer.h>
@ -91,15 +93,21 @@ class CChat : public CComponent
struct CCommand struct CCommand
{ {
const char *pName; const char *m_pName;
const char *pParams; const char *m_pParams;
bool operator<(const CCommand &Other) const { return str_comp(pName, Other.pName) < 0; } CCommand() {}
bool operator<=(const CCommand &Other) const { return str_comp(pName, Other.pName) <= 0; } CCommand(const char *pName, const char *pParams) :
bool operator==(const CCommand &Other) const { return str_comp(pName, Other.pName) == 0; } 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; bool m_ReverseTAB;
struct CHistoryEntry struct CHistoryEntry

View file

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

View file

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

View file

@ -81,15 +81,15 @@ void CCountryFlags::LoadCountryflagsIndexfile()
str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin); str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf);
} }
m_aCountryFlags.add_unsorted(CountryFlag); m_aCountryFlags.push_back(CountryFlag);
} }
io_close(File); io_close(File);
m_aCountryFlags.sort_range(); std::sort(m_aCountryFlags.begin(), m_aCountryFlags.end());
// find index of default item // find index of default item
int DefaultIndex = 0, Index = 0; size_t DefaultIndex = 0;
for(sorted_array<CCountryFlag>::range r = m_aCountryFlags.all(); !r.empty(); r.pop_front(), ++Index) for(size_t Index = 0; Index < m_aCountryFlags.size(); ++Index)
if(r.front().m_CountryCode == -1) if(m_aCountryFlags[Index].m_CountryCode == -1)
{ {
DefaultIndex = Index; DefaultIndex = Index;
break; break;
@ -97,11 +97,11 @@ void CCountryFlags::LoadCountryflagsIndexfile()
// init LUT // init LUT
if(DefaultIndex != 0) if(DefaultIndex != 0)
for(int &CodeIndexLUT : m_CodeIndexLUT) for(size_t &CodeIndexLUT : m_CodeIndexLUT)
CodeIndexLUT = DefaultIndex; CodeIndexLUT = DefaultIndex;
else else
mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT)); 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; m_CodeIndexLUT[maximum(0, (m_aCountryFlags[i].m_CountryCode - CODE_LB) % CODE_RANGE)] = i;
} }
@ -110,13 +110,13 @@ void CCountryFlags::OnInit()
// load country flags // load country flags
m_aCountryFlags.clear(); m_aCountryFlags.clear();
LoadCountryflagsIndexfile(); LoadCountryflagsIndexfile();
if(!m_aCountryFlags.size()) if(m_aCountryFlags.empty())
{ {
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "countryflags", "failed to load country flags. folder='countryflags/'"); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "countryflags", "failed to load country flags. folder='countryflags/'");
CCountryFlag DummyEntry; CCountryFlag DummyEntry;
DummyEntry.m_CountryCode = -1; DummyEntry.m_CountryCode = -1;
mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString)); mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString));
m_aCountryFlags.add(DummyEntry); m_aCountryFlags.push_back(DummyEntry);
} }
m_FlagsQuadContainerIndex = Graphics()->CreateQuadContainer(false); m_FlagsQuadContainerIndex = Graphics()->CreateQuadContainer(false);
@ -126,7 +126,7 @@ void CCountryFlags::OnInit()
Graphics()->QuadContainerUpload(m_FlagsQuadContainerIndex); Graphics()->QuadContainerUpload(m_FlagsQuadContainerIndex);
} }
int CCountryFlags::Num() const size_t CCountryFlags::Num() const
{ {
return m_aCountryFlags.size(); 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)]); 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) 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. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H #ifndef GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H
#define GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H #define GAME_CLIENT_COMPONENTS_COUNTRYFLAGS_H
#include <base/tl/sorted_array.h>
#include <base/vmath.h> #include <base/vmath.h>
#include <game/client/component.h> #include <game/client/component.h>
#include <vector>
class CCountryFlags : public CComponent class CCountryFlags : public CComponent
{ {
@ -21,9 +21,9 @@ public:
virtual int Sizeof() const override { return sizeof(*this); } virtual int Sizeof() const override { return sizeof(*this); }
void OnInit() override; void OnInit() override;
int Num() const; size_t Num() const;
const CCountryFlag *GetByCountryCode(int CountryCode) 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); void Render(int CountryCode, const ColorRGBA *pColor, float x, float y, float w, float h);
private: private:
@ -33,8 +33,8 @@ private:
CODE_UB = 999, CODE_UB = 999,
CODE_RANGE = CODE_UB - CODE_LB + 1, CODE_RANGE = CODE_UB - CODE_LB + 1,
}; };
sorted_array<CCountryFlag> m_aCountryFlags; std::vector<CCountryFlag> m_aCountryFlags;
int m_CodeIndexLUT[CODE_RANGE]; size_t m_CodeIndexLUT[CODE_RANGE];
int m_FlagsQuadContainerIndex; int m_FlagsQuadContainerIndex;

View file

@ -1,8 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* (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. */ /* 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/demo.h>
#include <engine/engine.h> #include <engine/engine.h>

View file

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

View file

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

View file

@ -221,8 +221,8 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
m_SelectedIndex = -1; m_SelectedIndex = -1;
// reset friend counter // 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) { 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(); 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 NameHash = str_quickhash(pItem->m_aClients[j].m_aName);
unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan); 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))) && 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))) &&
(!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)))) (!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++; Friend.m_NumFound++;
if(m_lFriends[f].m_pFriendInfo->m_aName[0]) if(Friend.m_pFriendInfo->m_aName[0])
break; break;
} }
} }
@ -1250,13 +1250,8 @@ void CMenus::FriendlistOnUpdate()
{ {
m_lFriends.clear(); m_lFriends.clear();
for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
{ m_lFriends.emplace_back(m_pClient->Friends()->GetFriend(i));
CFriendItem Item; std::sort(m_lFriends.begin(), m_lFriends.end());
Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i);
Item.m_NumFound = 0;
m_lFriends.add_unsorted(Item);
}
m_lFriends.sort_range();
} }
void CMenus::RenderServerbrowserFriends(CUIRect View) void CMenus::RenderServerbrowserFriends(CUIRect View)
@ -1286,14 +1281,14 @@ void CMenus::RenderServerbrowserFriends(CUIRect View)
// friends list(remove friend) // friends list(remove friend)
static float s_ScrollValue = 0; static float s_ScrollValue = 0;
if(m_FriendlistSelectedIndex >= m_lFriends.size()) if(m_FriendlistSelectedIndex >= (int)m_lFriends.size())
m_FriendlistSelectedIndex = m_lFriends.size() - 1; m_FriendlistSelectedIndex = m_lFriends.size() - 1;
UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
m_lFriends.sort_range(); std::sort(m_lFriends.begin(), m_lFriends.end());
for(int i = 0; i < m_lFriends.size(); ++i) 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) 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.VMargin(2.5f, &Item.m_Rect);
Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button); 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(&Item.m_Rect, Friend.m_pFriendInfo->m_aName, FontSize, TEXTALIGN_LEFT);
UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, 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.HMargin((OnState.h - FontSize) / 3, &OnState);
OnState.VMargin(5.0f, &OnState); OnState.VMargin(5.0f, &OnState);
char aBuf[64]; 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); 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_IsDir = IsDir != 0;
Item.m_StorageType = StorageType; Item.m_StorageType = StorageType;
pSelf->m_lDemos.add_unsorted(Item); pSelf->m_lDemos.push_back(Item);
if(tw::time_get() - pSelf->m_DemoPopulateStartTime > 500ms) if(tw::time_get() - pSelf->m_DemoPopulateStartTime > 500ms)
{ {
@ -788,7 +788,7 @@ void CMenus::DemolistPopulate()
if(g_Config.m_BrDemoFetchInfo) if(g_Config.m_BrDemoFetchInfo)
FetchAllHeaders(); FetchAllHeaders();
m_lDemos.sort_range(); std::stable_sort(m_lDemos.begin(), m_lDemos.end());
} }
void CMenus::DemolistOnUpdate(bool Reset) void CMenus::DemolistOnUpdate(bool Reset)
@ -800,11 +800,11 @@ void CMenus::DemolistOnUpdate(bool Reset)
bool Found = false; bool Found = false;
int SelectedIndex = -1; int SelectedIndex = -1;
// search for selected index // search for selected index
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front()) for(auto &Item : m_lDemos)
{ {
SelectedIndex++; 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; Found = true;
break; break;
@ -815,8 +815,8 @@ void CMenus::DemolistOnUpdate(bool Reset)
m_DemolistSelectedIndex = SelectedIndex; m_DemolistSelectedIndex = SelectedIndex;
} }
m_DemolistSelectedIndex = Reset ? m_lDemos.size() > 0 ? 0 : -1 : m_DemolistSelectedIndex = Reset ? !m_lDemos.empty() ? 0 : -1 :
m_DemolistSelectedIndex >= m_lDemos.size() ? m_lDemos.size() - 1 : m_DemolistSelectedIndex; m_DemolistSelectedIndex >= (int)m_lDemos.size() ? m_lDemos.size() - 1 : m_DemolistSelectedIndex;
m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_lDemos[m_DemolistSelectedIndex].m_IsDir; m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_lDemos[m_DemolistSelectedIndex].m_IsDir;
} }
@ -834,11 +834,11 @@ bool CMenus::FetchHeader(CDemoItem &Item)
void CMenus::FetchAllHeaders() 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) 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 // 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); DemolistOnUpdate(false);
} }
} }
@ -1095,13 +1095,13 @@ void CMenus::RenderDemoList(CUIRect MainView)
CUIRect OriginalView = ListBox; CUIRect OriginalView = ListBox;
int Num = (int)(ListBox.h / s_aCols[0].m_Rect.h) + 1; 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; ListBox.y -= s_ScrollValue * ScrollNum * s_aCols[0].m_Rect.h;
int ItemIndex = -1; int ItemIndex = -1;
bool DoubleClicked = false; bool DoubleClicked = false;
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front()) for(auto &Item : m_lDemos)
{ {
ItemIndex++; ItemIndex++;
@ -1126,10 +1126,10 @@ void CMenus::RenderDemoList(CUIRect MainView)
RenderTools()->DrawUIRect(&Rect, ColorRGBA(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f); 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; 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); DemolistOnUpdate(false);
m_DoubleClickIndex = ItemIndex; m_DoubleClickIndex = ItemIndex;
} }
@ -1152,7 +1152,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
if(ID == COL_ICON) 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) 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); 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; 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]; 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); Button.VMargin(4.0f, &Button);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT); 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]; char aBuf[32];
str_time((int64_t)Length * 100, TIME_HOURS, aBuf, sizeof(aBuf)); str_time((int64_t)Length * 100, TIME_HOURS, aBuf, sizeof(aBuf));
Button.VMargin(4.0f, &Button); Button.VMargin(4.0f, &Button);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT); 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]; 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); Button.VSplitRight(24.0f, &Button, 0);
UI()->DoLabelScaled(&Button, aBuf, 12.0f, TEXTALIGN_RIGHT); 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)); str_copy(Item.m_aPlayer, Info.m_aOwner, sizeof(Item.m_aPlayer));
Item.m_Time = Info.m_Time; Item.m_Time = Info.m_Time;
if(Item.m_Time > 0) if(Item.m_Time > 0)
pSelf->m_lGhosts.add(Item); pSelf->m_lGhosts.push_back(Item);
if(tw::time_get() - pSelf->m_GhostPopulateStartTime > 500ms) 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() void CMenus::GhostlistPopulate()
{ {
CGhostItem *pOwnGhost = 0;
m_lGhosts.clear(); m_lGhosts.clear();
m_GhostPopulateStartTime = tw::time_get(); m_GhostPopulateStartTime = tw::time_get();
Storage()->ListDirectory(IStorage::TYPE_ALL, m_pClient->m_Ghost.GetGhostDir(), GhostlistFetchCallback, this); 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++) CGhostItem *pOwnGhost = 0;
{ for(auto &Ghost : m_lGhosts)
if(str_comp(m_lGhosts[i].m_aPlayer, Client()->PlayerName()) == 0 && (!pOwnGhost || m_lGhosts[i] < *pOwnGhost)) if(str_comp(Ghost.m_aPlayer, Client()->PlayerName()) == 0 && (!pOwnGhost || Ghost < *pOwnGhost))
pOwnGhost = &m_lGhosts[i]; pOwnGhost = &Ghost;
}
if(pOwnGhost) if(pOwnGhost)
{ {
@ -950,16 +949,16 @@ void CMenus::GhostlistPopulate()
CMenus::CGhostItem *CMenus::GetOwnGhost() CMenus::CGhostItem *CMenus::GetOwnGhost()
{ {
for(int i = 0; i < m_lGhosts.size(); i++) for(auto &Ghost : m_lGhosts)
if(m_lGhosts[i].m_Own) if(Ghost.m_Own)
return &m_lGhosts[i]; return &Ghost;
return 0; return nullptr;
} }
void CMenus::UpdateOwnGhost(CGhostItem Item) void CMenus::UpdateOwnGhost(CGhostItem Item)
{ {
int Own = -1; 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) if(m_lGhosts[i].m_Own)
Own = i; Own = i;
@ -972,14 +971,14 @@ void CMenus::UpdateOwnGhost(CGhostItem Item)
} }
Item.m_Own = true; 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) void CMenus::DeleteGhostItem(int Index)
{ {
if(m_lGhosts[Index].HasFile()) if(m_lGhosts[Index].HasFile())
Storage()->RemoveFile(m_lGhosts[Index].m_aFilename, IStorage::TYPE_SAVE); 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) void CMenus::RenderGhost(CUIRect MainView)
@ -1158,7 +1157,7 @@ void CMenus::RenderGhost(CUIRect MainView)
GhostlistPopulate(); GhostlistPopulate();
} }
if(s_SelectedIndex == -1 || s_SelectedIndex >= m_lGhosts.size()) if(s_SelectedIndex == -1 || s_SelectedIndex >= (int)m_lGhosts.size())
return; return;
CGhostItem *pGhost = &m_lGhosts[s_SelectedIndex]; CGhostItem *pGhost = &m_lGhosts[s_SelectedIndex];

View file

@ -408,13 +408,13 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
int OldSelected = -1; int OldSelected = -1;
UiDoListboxStart(&s_ScrollValue, &MainView, 50.0f, Localize("Country / Region"), "", m_pClient->m_CountryFlags.Num(), 6, OldSelected, s_ScrollValue); 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); const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_CountryFlags.GetByIndex(i);
if(pEntry->m_CountryCode == *pCountry) if(pEntry->m_CountryCode == *pCountry)
OldSelected = i; 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) if(Item.m_Visible)
{ {
Item.m_Rect.Margin(5.0f, &Item.m_Rect); Item.m_Rect.Margin(5.0f, &Item.m_Rect);
@ -673,12 +673,12 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
// skin selector // skin selector
MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(230.0f - RenderEyesBelow * 25.0f, &SkinList, &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 int s_SkinCount = 0;
static float s_ScrollValue = 0.0f; static float s_ScrollValue = 0.0f;
if(s_InitSkinlist || m_pClient->m_Skins.Num() != s_SkinCount) 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) for(int i = 0; i < m_pClient->m_Skins.Num(); ++i)
{ {
const CSkin *s = m_pClient->m_Skins.Get(i); const CSkin *s = m_pClient->m_Skins.Get(i);
@ -698,22 +698,23 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
if(s == 0) if(s == 0)
continue; continue;
s_paSkinList.add(CUISkin(s)); s_vSkinList.emplace_back(s);
} }
std::sort(s_vSkinList.begin(), s_vSkinList.end());
s_InitSkinlist = false; s_InitSkinlist = false;
s_SkinCount = m_pClient->m_Skins.Num(); s_SkinCount = m_pClient->m_Skins.Num();
} }
int OldSelected = -1; int OldSelected = -1;
UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_paSkinList.size(), 4, OldSelected, s_ScrollValue); UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_vSkinList.size(), 4, OldSelected, s_ScrollValue);
for(int i = 0; i < s_paSkinList.size(); ++i) 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) if(str_comp(s->m_aName, pSkinName) == 0)
OldSelected = i; 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) if(Item.m_Visible)
{ {
CTeeRenderInfo Info = OwnSkinInfo; CTeeRenderInfo Info = OwnSkinInfo;
@ -728,10 +729,9 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
RenderTools()->RenderTee(pIdleState, &Info, Emote, vec2(1.0f, 0.0f), TeeRenderPos); RenderTools()->RenderTee(pIdleState, &Info, Emote, vec2(1.0f, 0.0f), TeeRenderPos);
Item.m_Rect.VSplitLeft(60.0f, 0, &Item.m_Rect); Item.m_Rect.VSplitLeft(60.0f, 0, &Item.m_Rect);
str_format(aBuf, sizeof(aBuf), "%s", s->m_aName);
SLabelProperties Props; SLabelProperties Props;
Props.m_MaxWidth = Item.m_Rect.w; 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) if(g_Config.m_Debug)
{ {
ColorRGBA BloodColor = *UseCustomColor ? color_cast<ColorRGBA>(ColorHSLA(*ColorBody)) : s->m_BloodColor; 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); const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
if(OldSelected != NewSelected) 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(); SetNeedSendInfo();
} }
@ -1704,7 +1704,7 @@ public:
bool operator<(const CLanguage &Other) const { return m_Name < Other.m_Name; } 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); IOHANDLE File = pStorage->OpenFile("languages/index.txt", IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL);
if(!File) if(!File)
@ -1759,7 +1759,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
char aFileName[IO_MAX_PATH_LENGTH]; char aFileName[IO_MAX_PATH_LENGTH];
str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin); 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); io_close(File);
} }
@ -1768,14 +1768,15 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
{ {
static int s_LanguageList = 0; static int s_LanguageList = 0;
static int s_SelectedLanguage = 0; static int s_SelectedLanguage = 0;
static sorted_array<CLanguage> s_Languages; static std::vector<CLanguage> s_Languages;
static float s_ScrollValue = 0; static float s_ScrollValue = 0;
if(s_Languages.size() == 0) if(s_Languages.empty())
{ {
s_Languages.add(CLanguage("English", "", 826)); s_Languages.emplace_back("English", "", 826);
LoadLanguageIndexfile(Storage(), Console(), &s_Languages); LoadLanguageIndexfile(Storage(), Console(), s_Languages);
for(int i = 0; i < s_Languages.size(); i++) 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) if(str_comp(s_Languages[i].m_FileName.c_str(), g_Config.m_ClLanguagefile) == 0)
{ {
s_SelectedLanguage = i; 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); 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) if(Item.m_Visible)
{ {
CUIRect Rect; CUIRect Rect;
@ -1798,9 +1798,9 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
Rect.VMargin(6.0f, &Rect); Rect.VMargin(6.0f, &Rect);
Rect.HMargin(3.0f, &Rect); Rect.HMargin(3.0f, &Rect);
ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f); 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); 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; SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, pName, sizeof(EntitiesItem.m_aName)); str_copy(EntitiesItem.m_aName, pName, sizeof(EntitiesItem.m_aName));
CMenus::LoadEntities(&EntitiesItem, pUser); CMenus::LoadEntities(&EntitiesItem, pUser);
pThis->m_EntitiesList.add(EntitiesItem); pThis->m_EntitiesList.push_back(EntitiesItem);
} }
else else
{ {
@ -116,7 +116,7 @@ int CMenus::EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser)
SCustomEntities EntitiesItem; SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, aName, sizeof(EntitiesItem.m_aName)); str_copy(EntitiesItem.m_aName, aName, sizeof(EntitiesItem.m_aName));
CMenus::LoadEntities(&EntitiesItem, pUser); 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> 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; auto *pRealUser = (SMenuAssetScanUser *)pUser;
if(IsDir) if(IsDir)
@ -177,7 +177,7 @@ static int AssetScan(const char *pName, int IsDir, int DirType, sorted_array<TNa
TName AssetItem; TName AssetItem;
str_copy(AssetItem.m_aName, pName, sizeof(AssetItem.m_aName)); str_copy(AssetItem.m_aName, pName, sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, pUser); LoadAsset(&AssetItem, pAssetName, pGraphics, pUser);
AssetList.add(AssetItem); AssetList.push_back(AssetItem);
} }
else else
{ {
@ -192,7 +192,7 @@ static int AssetScan(const char *pName, int IsDir, int DirType, sorted_array<TNa
TName AssetItem; TName AssetItem;
str_copy(AssetItem.m_aName, aName, sizeof(AssetItem.m_aName)); str_copy(AssetItem.m_aName, aName, sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, pUser); 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); return AssetScan(pName, IsDir, DirType, pThis->m_HudList, "hud", pGraphics, pUser);
} }
static sorted_array<const CMenus::SCustomEntities *> s_SearchEntitiesList; static std::vector<const CMenus::SCustomEntities *> s_SearchEntitiesList;
static sorted_array<const CMenus::SCustomGame *> s_SearchGamesList; static std::vector<const CMenus::SCustomGame *> s_SearchGamesList;
static sorted_array<const CMenus::SCustomEmoticon *> s_SearchEmoticonsList; static std::vector<const CMenus::SCustomEmoticon *> s_SearchEmoticonsList;
static sorted_array<const CMenus::SCustomParticle *> s_SearchParticlesList; static std::vector<const CMenus::SCustomParticle *> s_SearchParticlesList;
static sorted_array<const CMenus::SCustomHud *> s_SearchHudList; static std::vector<const CMenus::SCustomHud *> s_SearchHudList;
static const int NumberOfAssetsTabs = 5; static const int NumberOfAssetsTabs = 5;
static bool s_InitCustomList[NumberOfAssetsTabs] = { static bool s_InitCustomList[NumberOfAssetsTabs] = {
true, true,
}; };
static int s_CustomListSize[NumberOfAssetsTabs] = { static size_t s_CustomListSize[NumberOfAssetsTabs] = {
0, 0,
}; };
@ -252,7 +252,7 @@ static char s_aFilterString[NumberOfAssetsTabs][50];
static int s_CurCustomTab = ASSETS_TAB_ENTITIES; 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) if(CurTab == ASSETS_TAB_ENTITIES)
return s_SearchEntitiesList[Index]; return s_SearchEntitiesList[Index];
@ -269,9 +269,9 @@ static const CMenus::SCustomItem *GetCustomItem(int CurTab, int Index)
} }
template<typename TName> 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()) if(List[i].m_RenderTexture.IsValid())
pGraphics->UnloadTexture(&(List[i].m_RenderTexture)); pGraphics->UnloadTexture(&(List[i].m_RenderTexture));
@ -284,9 +284,9 @@ void CMenus::ClearCustomItems(int CurTab)
{ {
if(CurTab == ASSETS_TAB_ENTITIES) 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()) if(Image.m_Texture.IsValid())
Graphics()->UnloadTexture(&Image.m_Texture); Graphics()->UnloadTexture(&Image.m_Texture);
@ -330,24 +330,25 @@ void CMenus::ClearCustomItems(int CurTab)
} }
template<typename TName, typename TCaller> 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; TName AssetItem;
str_copy(AssetItem.m_aName, "default", sizeof(AssetItem.m_aName)); str_copy(AssetItem.m_aName, "default", sizeof(AssetItem.m_aName));
LoadAsset(&AssetItem, pAssetName, pGraphics, Caller); LoadAsset(&AssetItem, pAssetName, pGraphics, Caller);
AssetList.add(AssetItem); AssetList.push_back(AssetItem);
// load assets // load assets
pStorage->ListDirectory(IStorage::TYPE_ALL, pAssetPath, pfnCallback, Caller); pStorage->ListDirectory(IStorage::TYPE_ALL, pAssetPath, pfnCallback, Caller);
std::sort(AssetList.begin(), AssetList.end());
} }
if(AssetList.size() != s_CustomListSize[s_CurCustomTab]) if(AssetList.size() != s_CustomListSize[s_CurCustomTab])
s_InitCustomList[s_CurCustomTab] = true; s_InitCustomList[s_CurCustomTab] = true;
} }
template<typename TName> 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(); SearchList.clear();
int ListSize = AssetList.size(); 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])) if(s_aFilterString[s_CurCustomTab][0] != '\0' && !str_utf8_find_nocase(s->m_aName, s_aFilterString[s_CurCustomTab]))
continue; continue;
SearchList.add_unsorted(s); SearchList.push_back(s);
} }
return AssetList.size(); return AssetList.size();
} }
@ -397,15 +398,16 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
}; };
if(s_CurCustomTab == ASSETS_TAB_ENTITIES) if(s_CurCustomTab == ASSETS_TAB_ENTITIES)
{ {
if(m_EntitiesList.size() == 0) if(m_EntitiesList.empty())
{ {
SCustomEntities EntitiesItem; SCustomEntities EntitiesItem;
str_copy(EntitiesItem.m_aName, "default", sizeof(EntitiesItem.m_aName)); str_copy(EntitiesItem.m_aName, "default", sizeof(EntitiesItem.m_aName));
LoadEntities(&EntitiesItem, &User); LoadEntities(&EntitiesItem, &User);
m_EntitiesList.add(EntitiesItem); m_EntitiesList.push_back(EntitiesItem);
// load entities // load entities
Storage()->ListDirectory(IStorage::TYPE_ALL, "assets/entities", EntitiesScan, &User); 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]) if(m_EntitiesList.size() != s_CustomListSize[s_CurCustomTab])
s_InitCustomList[s_CurCustomTab] = true; 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])) if(s_aFilterString[s_CurCustomTab][0] != '\0' && !str_utf8_find_nocase(s->m_aName, s_aFilterString[s_CurCustomTab]))
continue; continue;
s_SearchEntitiesList.add_unsorted(s); s_SearchEntitiesList.push_back(s);
} }
} }
else if(s_CurCustomTab == ASSETS_TAB_GAME) else if(s_CurCustomTab == ASSETS_TAB_GAME)
@ -475,7 +477,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
float TextureWidth = 150; float TextureWidth = 150;
float TextureHeight = 150; float TextureHeight = 150;
int SearchListSize = 0; size_t SearchListSize = 0;
if(s_CurCustomTab == ASSETS_TAB_ENTITIES) 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); 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); const SCustomItem *s = GetCustomItem(s_CurCustomTab, i);
if(s == NULL) if(s == NULL)
@ -533,7 +535,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
OldSelected = i; OldSelected = i;
} }
CListboxItem Item = UiDoListboxNextItem(s, OldSelected == i); CListboxItem Item = UiDoListboxNextItem(s, OldSelected >= 0 && (size_t)OldSelected == i);
CUIRect ItemRect = Item.m_Rect; CUIRect ItemRect = Item.m_Rect;
ItemRect.Margin(Margin / 2, &ItemRect); ItemRect.Margin(Margin / 2, &ItemRect);
if(Item.m_Visible) 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. */ /* (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. */ /* 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/demo.h>
#include <engine/engine.h> #include <engine/engine.h>
#include <engine/graphics.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); 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; return 0;
} }
@ -324,24 +324,24 @@ void CSkins::OnInit()
void CSkins::Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc) 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(&Skin.m_OriginalSkin.m_Body);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_BodyOutline); Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_BodyOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_Feet); Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Feet);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_FeetOutline); Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_FeetOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_Hands); Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Hands);
Graphics()->UnloadTexture(&m_aSkins[i].m_OriginalSkin.m_HandsOutline); Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_HandsOutline);
for(auto &Eye : m_aSkins[i].m_OriginalSkin.m_Eyes) for(auto &Eye : Skin.m_OriginalSkin.m_Eyes)
Graphics()->UnloadTexture(&Eye); Graphics()->UnloadTexture(&Eye);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Body); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Body);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_BodyOutline); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_BodyOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Feet); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Feet);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_FeetOutline); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_FeetOutline);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_Hands); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Hands);
Graphics()->UnloadTexture(&m_aSkins[i].m_ColorableSkin.m_HandsOutline); Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_HandsOutline);
for(auto &Eye : m_aSkins[i].m_ColorableSkin.m_Eyes) for(auto &Eye : Skin.m_ColorableSkin.m_Eyes)
Graphics()->UnloadTexture(&Eye); Graphics()->UnloadTexture(&Eye);
} }
@ -351,14 +351,14 @@ void CSkins::Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc)
SkinScanUser.m_pThis = this; SkinScanUser.m_pThis = this;
SkinScanUser.m_SkinLoadedFunc = SkinLoadedFunc; SkinScanUser.m_SkinLoadedFunc = SkinLoadedFunc;
Storage()->ListDirectory(IStorage::TYPE_ALL, "skins", SkinScan, &SkinScanUser); 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/'"); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load skins. folder='skins/'");
CSkin DummySkin; CSkin DummySkin;
DummySkin.m_IsVanilla = true; DummySkin.m_IsVanilla = true;
str_copy(DummySkin.m_aName, "dummy", sizeof(DummySkin.m_aName)); str_copy(DummySkin.m_aName, "dummy", sizeof(DummySkin.m_aName));
DummySkin.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f); 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) int CSkins::FindImpl(const char *pName)
{ {
auto r = ::find_binary(m_aSkins.all(), pName); CSkin Needle;
if(!r.empty()) mem_zero(&Needle, sizeof(Needle));
return &r.front() - m_aSkins.base_ptr(); 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) if(str_comp(pName, "default") == 0)
return -1; return -1;
@ -415,20 +418,23 @@ int CSkins::FindImpl(const char *pName)
if(str_find(pName, "/") != 0) if(str_find(pName, "/") != 0)
return -1; return -1;
auto d = ::find_binary(m_aDownloadSkins.all(), pName); CDownloadSkin DownloadNeedle;
if(!d.empty()) 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]; char aPath[IO_MAX_PATH_LENGTH];
str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", d.front().m_aName); str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", RangeBegin->m_aName);
Storage()->RenameFile(d.front().m_aPath, aPath, IStorage::TYPE_SAVE); Storage()->RenameFile(RangeBegin->m_aPath, aPath, IStorage::TYPE_SAVE);
LoadSkin(d.front().m_aName, d.front().m_pTask->m_Info); LoadSkin(RangeBegin->m_aName, RangeBegin->m_pTask->m_Info);
d.front().m_pTask = nullptr; 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; 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)); 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); Skin.m_pTask = std::make_shared<CGetPngFile>(this, aUrl, Storage(), Skin.m_aPath);
m_pClient->Engine()->AddJob(Skin.m_pTask); 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; return -1;
} }

View file

@ -3,11 +3,11 @@
#ifndef GAME_CLIENT_COMPONENTS_SKINS_H #ifndef GAME_CLIENT_COMPONENTS_SKINS_H
#define GAME_CLIENT_COMPONENTS_SKINS_H #define GAME_CLIENT_COMPONENTS_SKINS_H
#include <base/color.h> #include <base/color.h>
#include <base/tl/sorted_array.h>
#include <base/vmath.h> #include <base/vmath.h>
#include <engine/shared/http.h> #include <engine/shared/http.h>
#include <game/client/component.h> #include <game/client/component.h>
#include <game/client/skin.h> #include <game/client/skin.h>
#include <vector>
class CSkins : public CComponent class CSkins : public CComponent
{ {
@ -46,8 +46,8 @@ public:
int Find(const char *pName); int Find(const char *pName);
private: private:
sorted_array<CSkin> m_aSkins; std::vector<CSkin> m_aSkins;
sorted_array<CDownloadSkin> m_aDownloadSkins; std::vector<CDownloadSkin> m_aDownloadSkins;
char m_EventSkinPrefix[24]; char m_EventSkinPrefix[24];
bool LoadSkinPNG(CImageInfo &Info, const char *pName, const char *pPath, int DirType); 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. */ /* (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. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/array.h>
#include "sounds.h" #include "sounds.h"
#include <engine/engine.h> #include <engine/engine.h>
#include <engine/shared/config.h> #include <engine/shared/config.h>

View file

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

View file

@ -4331,7 +4331,7 @@ static int EditorListdirCallback(const char *pName, int IsDir, int StorageType,
Item.m_IsDir = IsDir != 0; Item.m_IsDir = IsDir != 0;
Item.m_IsLink = false; Item.m_IsLink = false;
Item.m_StorageType = StorageType; Item.m_StorageType = StorageType;
pEditor->m_FileList.add(Item); pEditor->m_FileList.push_back(Item);
return 0; return 0;
} }
@ -4462,7 +4462,7 @@ void CEditor::RenderFileDialog()
m_FileDialogScrollValue = UIEx()->DoScrollbarV(&m_FileDialogScrollValue, &Scroll, m_FileDialogScrollValue); m_FileDialogScrollValue = UIEx()->DoScrollbarV(&m_FileDialogScrollValue, &Scroll, m_FileDialogScrollValue);
int ScrollNum = 0; 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; m_FileList[i].m_IsVisible = false;
if(!m_aFileDialogSearchText[0] || str_utf8_find_nocase(m_FileList[i].m_aName, m_aFileDialogSearchText)) 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) 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) if(m_FileList[NewIndex].m_IsVisible)
break; break;
@ -4513,7 +4513,7 @@ void CEditor::RenderFileDialog()
} }
} }
} }
if(NewIndex > -1 && NewIndex < m_FileList.size()) if(NewIndex > -1 && NewIndex < (int)m_FileList.size())
{ {
//scroll //scroll
float IndexY = View.y - m_FileDialogScrollValue * ScrollNum * 17.0f + NewIndex * 17.0f; 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_IsDir = true;
Item.m_IsLink = true; Item.m_IsLink = true;
Item.m_StorageType = IStorage::TYPE_SAVE; Item.m_StorageType = IStorage::TYPE_SAVE;
m_FileList.add(Item); m_FileList.push_back(Item);
} }
Storage()->ListDirectory(StorageType, m_pFileDialogPath, EditorListdirCallback, this); 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_PreviewImageIsLoaded = false;
m_FileDialogActivate = false; m_FileDialogActivate = false;

View file

@ -12,7 +12,6 @@
#include <base/tl/algorithm.h> #include <base/tl/algorithm.h>
#include <base/tl/array.h> #include <base/tl/array.h>
#include <base/tl/sorted_array.h>
#include <game/client/render.h> #include <game/client/render.h>
#include <game/client/ui.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; } 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_FilesStartAt;
int m_FilesCur; int m_FilesCur;
int m_FilesStopAt; int m_FilesStopAt;

View file

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

View file

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

View file

@ -2,9 +2,10 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_LOCALIZATION_H #ifndef GAME_LOCALIZATION_H
#define 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 <engine/shared/memheap.h>
#include <vector>
class CLocalizationDatabase class CLocalizationDatabase
{ {
@ -15,12 +16,18 @@ class CLocalizationDatabase
unsigned m_ContextHash; unsigned m_ContextHash;
const char *m_pReplacement; 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_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; } 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; CHeap m_StringsHeap;
int m_VersionCounter; int m_VersionCounter;
int m_CurrentVersion; int m_CurrentVersion;

View file

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