mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-18 14:08:19 +00:00
401 lines
12 KiB
C++
401 lines
12 KiB
C++
#ifndef GAME_EDITOR_EDITOR_SERVER_SETTINGS_H
|
|
#define GAME_EDITOR_EDITOR_SERVER_SETTINGS_H
|
|
|
|
#include "component.h"
|
|
#include "editor_ui.h"
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class CEditor;
|
|
struct SMapSettingInt;
|
|
struct SMapSettingCommand;
|
|
struct IMapSetting;
|
|
class CLineInput;
|
|
|
|
struct CEditorMapSetting
|
|
{
|
|
char m_aCommand[256];
|
|
|
|
CEditorMapSetting(const char *pCommand)
|
|
{
|
|
str_copy(m_aCommand, pCommand);
|
|
}
|
|
};
|
|
|
|
// A parsed map setting argument, storing the name and the type
|
|
// Used for validation and to display arguments names
|
|
struct SParsedMapSettingArg
|
|
{
|
|
char m_aName[32];
|
|
char m_Type;
|
|
};
|
|
|
|
// An argument for the current setting
|
|
struct SCurrentSettingArg
|
|
{
|
|
char m_aValue[160]; // Value of the argument
|
|
float m_X; // The X position
|
|
size_t m_Start; // Start offset within the input string
|
|
size_t m_End; // End offset within the input string
|
|
bool m_Error; // If the argument is wrong or not
|
|
char m_ExpectedType; // The expected type
|
|
};
|
|
|
|
struct SPossibleValueMatch
|
|
{
|
|
const char *m_pValue; // Possible value string
|
|
int m_ArgIndex; // Argument for that possible value
|
|
const void *m_pData; // Generic pointer to pass specific data
|
|
};
|
|
|
|
struct SCommandParseError
|
|
{
|
|
char m_aMessage[256];
|
|
int m_ArgIndex;
|
|
};
|
|
|
|
struct SInvalidSetting
|
|
{
|
|
enum
|
|
{
|
|
TYPE_INVALID = 1 << 0,
|
|
TYPE_DUPLICATE = 1 << 1
|
|
};
|
|
int m_Index; // Index of the command in the loaded map settings list
|
|
char m_aSetting[256]; // String of the setting
|
|
int m_Type; // Type of that invalid setting
|
|
int m_CollidingIndex; // The colliding line index in case type is TYPE_DUPLICATE
|
|
|
|
struct SContext
|
|
{
|
|
bool m_Fixed;
|
|
bool m_Deleted;
|
|
bool m_Chosen;
|
|
} m_Context;
|
|
|
|
SInvalidSetting(int Index, const char *pSetting, int Type, int CollidingIndex) :
|
|
m_Index(Index), m_Type(Type), m_CollidingIndex(CollidingIndex), m_Context()
|
|
{
|
|
str_copy(m_aSetting, pSetting);
|
|
}
|
|
};
|
|
|
|
// --------------------------------------
|
|
// Builder classes & methods to generate list of possible values
|
|
// easily for specific settings and arguments.
|
|
// It uses a container stored inside CMapSettingsBackend.
|
|
// Usage:
|
|
// CValuesBuilder Builder(&m_Container);
|
|
// // Either do it in one go:
|
|
// Builder("tune").Argument(0).Add("value_1").Add("value_2");
|
|
// // Or reference the builder (useful when using in a loop):
|
|
// auto TuneBuilder = Builder("tune").Argument(0);
|
|
// TuneBuilder.Add("value_1");
|
|
// TuneBuilder.Add("value_2");
|
|
// // ...
|
|
|
|
using TArgumentValuesList = std::vector<const char *>; // List of possible values
|
|
using TSettingValues = std::map<int, TArgumentValuesList>; // Possible values per argument
|
|
using TSettingsArgumentValues = std::map<std::string, TSettingValues>; // Possible values per argument, per command/setting name
|
|
|
|
class CValuesBuilder;
|
|
class CSettingValuesBuilder;
|
|
class CArgumentValuesListBuilder
|
|
{
|
|
public:
|
|
CArgumentValuesListBuilder &Add(const char *pString)
|
|
{
|
|
m_pContainer->emplace_back(pString);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
CArgumentValuesListBuilder(std::vector<const char *> *pContainer) :
|
|
m_pContainer(pContainer) {}
|
|
|
|
std::vector<const char *> *m_pContainer;
|
|
friend class CSettingValuesBuilder;
|
|
};
|
|
|
|
class CSettingValuesBuilder
|
|
{
|
|
public:
|
|
CArgumentValuesListBuilder Argument(int Arg) const
|
|
{
|
|
return CArgumentValuesListBuilder(&(*m_pContainer)[Arg]);
|
|
}
|
|
|
|
private:
|
|
CSettingValuesBuilder(TSettingValues *pContainer) :
|
|
m_pContainer(pContainer) {}
|
|
|
|
friend class CValuesBuilder;
|
|
TSettingValues *m_pContainer;
|
|
};
|
|
|
|
class CValuesBuilder
|
|
{
|
|
public:
|
|
CValuesBuilder(TSettingsArgumentValues *pContainer) :
|
|
m_pContainer(pContainer)
|
|
{
|
|
}
|
|
|
|
CSettingValuesBuilder operator()(const char *pSettingName) const
|
|
{
|
|
return CSettingValuesBuilder(&(*m_pContainer)[pSettingName]);
|
|
}
|
|
|
|
private:
|
|
TSettingsArgumentValues *m_pContainer;
|
|
};
|
|
|
|
// --------------------------------------
|
|
|
|
struct SValueLoader
|
|
{
|
|
static void LoadTuneValues(const CSettingValuesBuilder &TuneBuilder);
|
|
static void LoadTuneZoneValues(const CSettingValuesBuilder &TuneZoneBuilder);
|
|
static void LoadMapBugs(const CSettingValuesBuilder &BugBuilder);
|
|
static void LoadArgumentTuneValues(CArgumentValuesListBuilder &&ArgBuilder);
|
|
};
|
|
|
|
enum class EValidationResult
|
|
{
|
|
VALID = 0,
|
|
ERROR,
|
|
INCOMPLETE,
|
|
UNKNOWN,
|
|
OUT_OF_RANGE,
|
|
};
|
|
|
|
enum class ECollisionCheckResult
|
|
{
|
|
ERROR,
|
|
REPLACE,
|
|
ADD
|
|
};
|
|
|
|
class CMapSettingsBackend : public CEditorComponent
|
|
{
|
|
typedef void (*FLoaderFunction)(const CSettingValuesBuilder &);
|
|
|
|
public: // General methods
|
|
CMapSettingsBackend() = default;
|
|
|
|
void Init(CEditor *pEditor) override;
|
|
bool OnInput(const IInput::CEvent &Event) override;
|
|
void OnUpdate() override;
|
|
void OnMapLoad() override;
|
|
|
|
public: // Constraints methods
|
|
enum class EArgConstraint
|
|
{
|
|
DEFAULT = 0,
|
|
UNIQUE,
|
|
MULTIPLE,
|
|
};
|
|
|
|
EArgConstraint ArgConstraint(const char *pSettingName, int Arg) const
|
|
{
|
|
return m_ArgConstraintsPerCommand.at(pSettingName).at(Arg);
|
|
}
|
|
|
|
public: // Backend methods
|
|
const std::vector<SParsedMapSettingArg> &ParsedArgs(const std::shared_ptr<IMapSetting> &pSetting) const
|
|
{
|
|
return m_ParsedCommandArgs.at(pSetting);
|
|
}
|
|
|
|
public: // CContext
|
|
class CContext
|
|
{
|
|
static const ColorRGBA ms_ArgumentStringColor;
|
|
static const ColorRGBA ms_ArgumentNumberColor;
|
|
static const ColorRGBA ms_ArgumentUnknownColor;
|
|
static const ColorRGBA ms_ErrorColor;
|
|
|
|
friend class CMapSettingsBackend;
|
|
|
|
public:
|
|
bool CommandIsValid() const { return m_pCurrentSetting != nullptr; }
|
|
int CurrentArg() const { return m_CursorArgIndex; }
|
|
const char *CurrentArgName() const { return (!m_pCurrentSetting || m_CursorArgIndex < 0 || m_CursorArgIndex >= (int)m_pBackend->m_ParsedCommandArgs.at(m_pCurrentSetting).size()) ? nullptr : m_pBackend->m_ParsedCommandArgs.at(m_pCurrentSetting).at(m_CursorArgIndex).m_aName; }
|
|
float CurrentArgPos() const { return m_CursorArgIndex == -1 ? 0 : m_vCurrentArgs[m_CursorArgIndex].m_X; }
|
|
size_t CurrentArgOffset() const { return m_CursorArgIndex == -1 ? 0 : m_vCurrentArgs[m_CursorArgIndex].m_Start; }
|
|
const char *CurrentArgValue() const { return m_CursorArgIndex == -1 ? m_aCommand : m_vCurrentArgs[m_CursorArgIndex].m_aValue; }
|
|
const std::vector<SPossibleValueMatch> &PossibleMatches() const { return m_vPossibleMatches; }
|
|
bool HasError() const { return m_Error.m_aMessage[0] != '\0'; }
|
|
size_t ErrorOffset() const { return m_Error.m_ArgIndex < 0 ? 0 : m_vCurrentArgs.at(m_Error.m_ArgIndex).m_Start; }
|
|
const char *Error() const { return m_Error.m_aMessage; }
|
|
int ArgCount() const { return (int)m_vCurrentArgs.size(); }
|
|
const SCurrentSettingArg &Arg(int Index) const { return m_vCurrentArgs.at(Index); }
|
|
const std::shared_ptr<IMapSetting> &Setting() const { return m_pCurrentSetting; }
|
|
CLineInput *LineInput() const { return m_pLineInput; }
|
|
void SetFontSize(float FontSize) { m_FontSize = FontSize; }
|
|
|
|
int CheckCollision(ECollisionCheckResult &Result) const;
|
|
int CheckCollision(const std::vector<CEditorMapSetting> &vSettings, ECollisionCheckResult &Result) const;
|
|
int CheckCollision(const char *pInputString, const std::vector<CEditorMapSetting> &vSettings, ECollisionCheckResult &Result) const;
|
|
|
|
void Update();
|
|
void UpdateFromString(const char *pStr);
|
|
bool UpdateCursor(bool Force = false);
|
|
void Reset();
|
|
void GetCommandHelpText(char *pStr, int Length) const;
|
|
bool Valid() const;
|
|
void ColorArguments(std::vector<STextColorSplit> &vColorSplits) const;
|
|
|
|
bool m_AllowUnknownCommands;
|
|
SEditBoxDropdownContext m_DropdownContext;
|
|
int m_CurrentCompletionIndex;
|
|
|
|
private: // Methods
|
|
CContext(CMapSettingsBackend *pMaster, CLineInput *pLineinput) :
|
|
m_DropdownContext(), m_pLineInput(pLineinput), m_pBackend(pMaster)
|
|
{
|
|
m_AllowUnknownCommands = false;
|
|
Reset();
|
|
}
|
|
|
|
void ClearError();
|
|
EValidationResult ValidateArg(int Index, const char *pArg);
|
|
void UpdatePossibleMatches();
|
|
void ParseArgs(const char *pLineInputStr, const char *pStr);
|
|
bool OnInput(const IInput::CEvent &Event);
|
|
const char *InputString() const;
|
|
void UpdateCompositionString();
|
|
|
|
template<int N>
|
|
void FormatDisplayValue(const char *pValue, char (&aOut)[N]);
|
|
|
|
private: // Fields
|
|
std::shared_ptr<IMapSetting> m_pCurrentSetting; // Current setting, can be nullptr in case of invalid setting name
|
|
std::vector<SCurrentSettingArg> m_vCurrentArgs; // Current parsed arguments from lineinput string
|
|
int m_CursorArgIndex; // The current argument the cursor is over
|
|
std::vector<SPossibleValueMatch> m_vPossibleMatches; // The current matches from cursor argument
|
|
size_t m_LastCursorOffset; // Last cursor offset
|
|
CLineInput *m_pLineInput;
|
|
char m_aCommand[128]; // The current command, not necessarily valid
|
|
SCommandParseError m_Error; // Error
|
|
|
|
CMapSettingsBackend *m_pBackend;
|
|
std::string m_CompositionStringBuffer;
|
|
float m_FontSize;
|
|
};
|
|
|
|
CContext NewContext(CLineInput *pLineInput)
|
|
{
|
|
return CContext(this, pLineInput);
|
|
}
|
|
|
|
private: // Loader methods
|
|
void LoadAllMapSettings();
|
|
void LoadCommand(const char *pName, const char *pArgs, const char *pHelp);
|
|
void LoadSettingInt(const std::shared_ptr<SMapSettingInt> &pSetting);
|
|
void LoadSettingCommand(const std::shared_ptr<SMapSettingCommand> &pSetting);
|
|
void InitValueLoaders();
|
|
void LoadPossibleValues(const CSettingValuesBuilder &Builder, const std::shared_ptr<IMapSetting> &pSetting);
|
|
void RegisterLoader(const char *pSettingName, const FLoaderFunction &pfnLoader);
|
|
void LoadConstraints();
|
|
|
|
static void PossibleConfigVariableCallback(const struct SConfigVariable *pVariable, void *pUserData);
|
|
|
|
private: // Argument constraints
|
|
using TArgumentConstraints = std::map<int, EArgConstraint>; // Constraint per argument index
|
|
using TCommandArgumentConstraints = std::map<std::string, TArgumentConstraints>; // Constraints per command/setting name
|
|
|
|
// Argument constraints builder
|
|
// Used to define arguments constraints for specific commands
|
|
// It uses a container stored in CMapSettingsBackend.
|
|
// Usage:
|
|
// CCommandArgumentConstraintBuilder Command(&m_Container);
|
|
// Command("tune", 2).Unique(0); // Defines argument 0 of command "tune" having 2 args as UNIQUE
|
|
// Command("tune_zone", 3).Multiple(0).Unique(1);
|
|
// // ^ Multiple() currently is only for readable purposes. It can be omited:
|
|
// // Command("tune_zone", 3).Unique(1);
|
|
//
|
|
|
|
class CCommandArgumentConstraintBuilder;
|
|
|
|
class CArgumentConstraintsBuilder
|
|
{
|
|
friend class CCommandArgumentConstraintBuilder;
|
|
|
|
private:
|
|
CArgumentConstraintsBuilder(TArgumentConstraints *pContainer) :
|
|
m_pContainer(pContainer){};
|
|
|
|
TArgumentConstraints *m_pContainer;
|
|
|
|
public:
|
|
CArgumentConstraintsBuilder &Multiple(int Arg)
|
|
{
|
|
// Define a multiple argument constraint
|
|
(*m_pContainer)[Arg] = EArgConstraint::MULTIPLE;
|
|
return *this;
|
|
}
|
|
|
|
CArgumentConstraintsBuilder &Unique(int Arg)
|
|
{
|
|
// Define a unique argument constraint
|
|
(*m_pContainer)[Arg] = EArgConstraint::UNIQUE;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class CCommandArgumentConstraintBuilder
|
|
{
|
|
public:
|
|
CCommandArgumentConstraintBuilder(TCommandArgumentConstraints *pContainer) :
|
|
m_pContainer(pContainer) {}
|
|
|
|
CArgumentConstraintsBuilder operator()(const char *pSettingName, int ArgCount)
|
|
{
|
|
for(int i = 0; i < ArgCount; i++)
|
|
(*m_pContainer)[pSettingName][i] = EArgConstraint::DEFAULT;
|
|
return CArgumentConstraintsBuilder(&(*m_pContainer)[pSettingName]);
|
|
}
|
|
|
|
private:
|
|
TCommandArgumentConstraints *m_pContainer;
|
|
};
|
|
|
|
TCommandArgumentConstraints m_ArgConstraintsPerCommand;
|
|
|
|
private: // Backend fields
|
|
std::vector<std::shared_ptr<IMapSetting>> m_vpMapSettings;
|
|
std::map<std::shared_ptr<IMapSetting>, std::vector<SParsedMapSettingArg>> m_ParsedCommandArgs; // Parsed available settings arguments, used for validation
|
|
TSettingsArgumentValues m_PossibleValuesPerCommand;
|
|
std::map<std::string, FLoaderFunction> m_LoaderFunctions;
|
|
|
|
static CContext *ms_pActiveContext;
|
|
|
|
friend class CEditor;
|
|
|
|
private: // Map settings validation on load
|
|
struct SLoadedMapSettings
|
|
{
|
|
std::vector<SInvalidSetting> m_vSettingsInvalid;
|
|
std::vector<CEditorMapSetting> m_vSettingsValid;
|
|
std::map<int, std::vector<int>> m_SettingsDuplicate;
|
|
|
|
SLoadedMapSettings() :
|
|
m_vSettingsInvalid(), m_vSettingsValid(), m_SettingsDuplicate()
|
|
{
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
m_vSettingsInvalid.clear();
|
|
m_vSettingsValid.clear();
|
|
m_SettingsDuplicate.clear();
|
|
}
|
|
|
|
} m_LoadedMapSettings;
|
|
};
|
|
|
|
#endif
|