From 961e7f58c409b17055c3718118c4eefae5558154 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Dec 2019 02:23:26 +0100 Subject: [PATCH] Implement UI errors --- CMakeLists.txt | 1 + SourceX/DiabloUI/diabloui.cpp | 22 +++++++++ SourceX/DiabloUI/diabloui.h | 1 + SourceX/DiabloUI/dialogs.cpp | 45 +++--------------- SourceX/DiabloUI/mainmenu.cpp | 43 ++++++++++-------- SourceX/DiabloUI/progress.cpp | 19 +++++++- SourceX/DiabloUI/selgame.cpp | 57 +++++++++++++++++++---- SourceX/DiabloUI/selhero.cpp | 40 ++++++++++++---- SourceX/DiabloUI/selok.cpp | 86 +++++++++++++++++++++++++++++++++++ SourceX/DiabloUI/selok.h | 11 +++++ SourceX/DiabloUI/selyesno.cpp | 3 +- SourceX/DiabloUI/selyesno.h | 2 +- SourceX/dvlnet/abstract_net.h | 5 ++ SourceX/dvlnet/frame_queue.h | 5 ++ SourceX/dvlnet/packet.h | 15 ++++-- SourceX/dvlnet/tcp_client.cpp | 12 +++-- SourceX/dvlnet/tcp_server.cpp | 1 + SourceX/dvlnet/tcp_server.h | 5 ++ SourceX/dvlnet/udp_p2p.cpp | 15 +++--- 19 files changed, 295 insertions(+), 93 deletions(-) create mode 100644 SourceX/DiabloUI/selok.cpp create mode 100644 SourceX/DiabloUI/selok.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 508f9d966..bbe49cc3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,7 @@ set(devilutionx_SRCS SourceX/DiabloUI/selgame.cpp SourceX/DiabloUI/selhero.cpp SourceX/DiabloUI/selyesno.cpp + SourceX/DiabloUI/selok.cpp SourceX/DiabloUI/text_draw.cpp SourceX/DiabloUI/text.cpp SourceX/DiabloUI/title.cpp diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index b346253d8..95d86d345 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -36,6 +36,7 @@ Art ArtBackground; Art ArtCursor; Art ArtHero; bool gbSpawned; +int heroLevel; void (*gfnSoundFunction)(char *file); void (*gfnListFocus)(int value); @@ -416,6 +417,27 @@ BOOL UiValidPlayerName(char *name) if (*letter < 0x20 || (*letter > 0x7E && *letter < 0xC0)) return false; + char *reserved[] = { + "gvdl", + "dvou", + "tiju", + "cjudi", + "bttipmf", + "ojhhfs", + "cmj{{bse", + "benjo", + }; + + char tmpname[PLR_NAME_LEN]; + strcpy(tmpname, name); + for (size_t i = 0, n = strlen(tmpname); i < n; i++) + tmpname[i]++; + + for (int i = 0; i < sizeof(reserved) / sizeof(*reserved); i++) { + if (strstr(tmpname, reserved[i])) + return false; + } + return true; } diff --git a/SourceX/DiabloUI/diabloui.h b/SourceX/DiabloUI/diabloui.h index 1a710750b..79de800fd 100644 --- a/SourceX/DiabloUI/diabloui.h +++ b/SourceX/DiabloUI/diabloui.h @@ -26,6 +26,7 @@ extern Art ArtBackground; extern Art ArtCursor; extern Art ArtHero; extern bool gbSpawned; +extern int heroLevel; constexpr auto MAINMENU_BACKGROUND = UiImage(&ArtBackground, { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }); constexpr auto MAINMENU_LOGO = UiImage(&ArtLogos[LOGO_MED], /*animated=*/true, /*frame=*/0, { 0, 0, 0, 0 }, UIS_CENTER); diff --git a/SourceX/DiabloUI/dialogs.cpp b/SourceX/DiabloUI/dialogs.cpp index 14e9c5f7b..a1236a397 100644 --- a/SourceX/DiabloUI/dialogs.cpp +++ b/SourceX/DiabloUI/dialogs.cpp @@ -16,7 +16,6 @@ extern SDL_Surface *pal_surface; namespace { Art dialogArt; -Art progressArt; char dialogText[256]; char dialogCaption[1024]; bool fontWasLoaded; @@ -25,29 +24,17 @@ bool textInputWasActive; UiItem *dialogItems; std::size_t dialogItemsSize; -enum class State { - DEFAULT = 0, - OK, - CANCEL, -}; - -State state; +bool dialogEnd; void DialogActionOK() { - state = State::OK; -} - -void DialogActionCancel() -{ - state = State::CANCEL; + dialogEnd = true; } -constexpr auto DIALOG_ART_S = UiImage(&dialogArt, { 180, 168, 280, 144 }); constexpr auto DIALOG_ART_L = UiImage(&dialogArt, { 127, 100, 385, 280 }); UiItem OK_DIALOG[] = { - DIALOG_ART_S, + UiImage(&dialogArt, { 180, 168, 280, 144 }), UiText(dialogText, { 200, 211, 240, 80 }, UIS_CENTER), MakeSmlButton("OK", &DialogActionOK, 265, 265), }; @@ -59,26 +46,6 @@ UiItem OK_DIALOG_WITH_CAPTION[] = { MakeSmlButton("OK", &DialogActionOK, 264, 335), }; -UiItem PROGRESS_DIALOG[] = { - DIALOG_ART_S, - UiText(dialogText, { 180, 177, 280, 43 }, UIS_CENTER), - UiImage(&progressArt, { 205, 220, 228, 38 }), - MakeSmlButton("Cancel", &DialogActionCancel, 330, 265), -}; - -UiListItem SELOK_DIALOG_ITEMS[] = { - { "OK", 0 } -}; -UiItem SELOK_DIALOG[] = { - UiText(dialogText, { 140, 210, 400, 168 }, UIS_CENTER), - UiList(SELOK_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER), -}; - -UiItem SPAWNERR_DIALOG[] = { - UiText("The Rogue and Sorcerer are only available in the full retail version of Diablo. For ordering information visit https://www.gog.com/game/diablo.", { 140, 199, 400, 177 }), - UiArtTextButton("OK", &DialogActionOK, { 230, 407, 180, 43 }), -}; - // clang-format off #define BLANKCOLOR { 0, 0xFF, 0, 0 } // clang-format on @@ -248,7 +215,7 @@ void Deinit() void DialogLoop(UiItem *items, std::size_t num_items, UiItem *render_behind, std::size_t render_behind_size) { SDL_Event event; - state = State::DEFAULT; + dialogEnd = false; if (render_behind_size == 0) { LoadBackgroundArt("ui_art\\black.pcx"); if (ArtBackground.surface == nullptr) { @@ -266,7 +233,7 @@ void DialogLoop(UiItem *items, std::size_t num_items, UiItem *render_behind, std switch (GetMenuAction(event)) { case MenuAction::BACK: case MenuAction::SELECT: - state = State::OK; + dialogEnd = true; break; default: break; @@ -284,7 +251,7 @@ void DialogLoop(UiItem *items, std::size_t num_items, UiItem *render_behind, std UiRenderItems(items, num_items); DrawMouse(); UiFadeIn(); - } while (state == State::DEFAULT); + } while (!dialogEnd); } } // namespace diff --git a/SourceX/DiabloUI/mainmenu.cpp b/SourceX/DiabloUI/mainmenu.cpp index 91e04a2f2..8e145eb49 100644 --- a/SourceX/DiabloUI/mainmenu.cpp +++ b/SourceX/DiabloUI/mainmenu.cpp @@ -1,5 +1,6 @@ #include "devilution.h" #include "DiabloUI/diabloui.h" +#include "DiabloUI/selok.h" namespace dvl { @@ -8,11 +9,11 @@ DWORD dwAttractTicks; int MainMenuResult; UiListItem MAINMENU_DIALOG_ITEMS[] = { - {"Single Player", MAINMENU_SINGLE_PLAYER}, - {"Multi Player", MAINMENU_MULTIPLAYER}, - {"Replay Intro", MAINMENU_REPLAY_INTRO}, - {"Show Credits", MAINMENU_SHOW_CREDITS}, - {"Exit Diablo", MAINMENU_EXIT_DIABLO} + { "Single Player", MAINMENU_SINGLE_PLAYER }, + { "Multi Player", MAINMENU_MULTIPLAYER }, + { "Replay Intro", MAINMENU_REPLAY_INTRO }, + { "Show Credits", MAINMENU_SHOW_CREDITS }, + { "Exit Diablo", MAINMENU_EXIT_DIABLO } }; UiItem MAINMENU_DIALOG[] = { MAINMENU_BACKGROUND, @@ -41,8 +42,6 @@ void mainmenu_Load(char *name, void (*fnSound)(char *file)) gfnSoundFunction = fnSound; MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].art_text.text = name; - MainMenuResult = 0; - if (!gbSpawned) { LoadBackgroundArt("ui_art\\mainmenu.pcx"); } else { @@ -59,23 +58,31 @@ void mainmenu_Free() BOOL UiMainMenuDialog(char *name, int *pdwResult, void (*fnSound)(char *file), int attractTimeOut) { - mainmenu_attract_time_out = attractTimeOut; - mainmenu_Load(name, fnSound); + MainMenuResult = 0; + while (MainMenuResult == 0) { + mainmenu_attract_time_out = attractTimeOut; + mainmenu_Load(name, fnSound); - mainmenu_restart_repintro(); // for automatic starts + mainmenu_restart_repintro(); // for automatic starts - while (MainMenuResult == 0) { - UiPollAndRender(); - if (GetTickCount() >= dwAttractTicks) { - MainMenuResult = MAINMENU_ATTRACT_MODE; + while (MainMenuResult == 0) { + UiPollAndRender(); + if (!gbSpawned && GetTickCount() >= dwAttractTicks) { + MainMenuResult = MAINMENU_ATTRACT_MODE; + } } - } - BlackPalette(); - mainmenu_Free(); + BlackPalette(); + mainmenu_Free(); + + if (gbSpawned && MainMenuResult == MAINMENU_REPLAY_INTRO) { + UiSelOkDialog(nullptr, "The Diablo introduction cinematic is only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.", true); + MainMenuResult = 0; + } + } *pdwResult = MainMenuResult; return true; } -} +} // namespace dvl diff --git a/SourceX/DiabloUI/progress.cpp b/SourceX/DiabloUI/progress.cpp index c59119530..3383b4128 100644 --- a/SourceX/DiabloUI/progress.cpp +++ b/SourceX/DiabloUI/progress.cpp @@ -8,12 +8,29 @@ namespace dvl { +Art dialogArt; +char dialogText[256]; +Art progressArt; Art ArtPopupSm; Art ArtProgBG; Art ProgFil; SDL_Surface *msgSurface; SDL_Surface *cancleSurface; int textWidth; +bool endMenu; + +void DialogActionCancel() +{ + endMenu = true; +} + +// TODO use PROGRESS_DIALOG for rendering the progressbar or delete it +UiItem PROGRESS_DIALOG[] = { + UiImage(&dialogArt, { 180, 168, 280, 144 }), + UiText(dialogText, { 180, 177, 280, 43 }, UIS_CENTER), + UiImage(&progressArt, { 205, 220, 228, 38 }), + MakeSmlButton("Cancel", &DialogActionCancel, 330, 265), +}; void progress_Load(char *msg) { @@ -82,7 +99,7 @@ BOOL UiProgressDialog(HWND window, char *msg, int enable, int (*fnfunc)(), int r { progress_Load(msg); - bool endMenu = false; + endMenu = false; int progress = 0; SDL_Event event; diff --git a/SourceX/DiabloUI/selgame.cpp b/SourceX/DiabloUI/selgame.cpp index 6e20d0f22..e43e946bb 100644 --- a/SourceX/DiabloUI/selgame.cpp +++ b/SourceX/DiabloUI/selgame.cpp @@ -5,6 +5,7 @@ #include "DiabloUI/diabloui.h" #include "DiabloUI/text.h" #include "DiabloUI/dialogs.h" +#include "DiabloUI/selok.h" namespace dvl { @@ -26,6 +27,9 @@ constexpr UiArtTextButton SELGAME_CANCEL = UiArtTextButton("CANCEL", &UiFocusNav UiArtText SELGAME_DESCRIPTION(selgame_Description, { 35, 256, 205, 192 }); +namespace { + +char title[32]; UiListItem SELDIFF_DIALOG_ITEMS[] = { { "Normal", DIFF_NORMAL }, { "Nightmare", DIFF_NIGHTMARE }, @@ -34,7 +38,7 @@ UiListItem SELDIFF_DIALOG_ITEMS[] = { UiItem SELDIFF_DIALOG[] = { MAINMENU_BACKGROUND, MAINMENU_LOGO, - UiArtText("Create Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiArtText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), UiArtText(selgame_Label, { 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF SELGAME_DESCRIPTION, UiArtText("Select Difficulty", { 299, 211, 295, 35 }, UIS_CENTER | UIS_BIG), @@ -43,7 +47,7 @@ UiItem SELDIFF_DIALOG[] = { SELGAME_CANCEL, }; -constexpr UiArtText SELUDPGAME_TITLE = UiArtText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG); +constexpr UiArtText SELUDPGAME_TITLE = UiArtText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG); constexpr UiArtText SELUDPGAME_DESCRIPTION_LABEL = UiArtText("Description:", { 35, 211, 205, 192 }, UIS_MED); UiListItem SELUDPGAME_DIALOG_ITEMS[] = { @@ -86,6 +90,8 @@ UiItem ENTERPASSWORD_DIALOG[] = { SELGAME_CANCEL, }; +} // namespace + void selgame_Free() { ArtBackground.Unload(); @@ -103,6 +109,7 @@ void selgame_GameSelection_Init() } getIniValue("Phone Book", "Entry1", selgame_Ip, 128); + strcpy(title, "Client-Server (TCP)"); UiInitList(0, 1, selgame_GameSelection_Focus, selgame_GameSelection_Select, selgame_GameSelection_Esc, SELUDPGAME_DIALOG, size(SELUDPGAME_DIALOG)); } @@ -126,9 +133,11 @@ void selgame_GameSelection_Select(int value) switch (value) { case 0: + strcpy(title, "Create Game"); UiInitList(0, NUM_DIFFICULTIES - 1, selgame_Diff_Focus, selgame_Diff_Select, selgame_Diff_Esc, SELDIFF_DIALOG, size(SELDIFF_DIALOG)); break; case 1: + strcpy(title, "Join TCP Games"); UiInitList(0, 0, NULL, selgame_Password_Init, selgame_GameSelection_Init, ENTERIP_DIALOG, size(ENTERIP_DIALOG)); break; } @@ -160,8 +169,32 @@ void selgame_Diff_Focus(int value) WordWrapArtStr(selgame_Description, SELGAME_DESCRIPTION.rect.w); } +bool IsDifficultyAllowed(int value) +{ + if (value == 0 || (value == 1 && heroLevel >= 20) || (value == 2 && heroLevel >= 30)) { + return true; + } + + selgame_Free(); + BlackPalette(); + + if (value == 1) + UiSelOkDialog(title, "Your character must reach level 20 before you can enter a multiplayer game of Nightmare difficulty.", false); + if (value == 2) + UiSelOkDialog(title, "Your character must reach level 30 before you can enter a multiplayer game of Hell difficulty.", false); + + LoadBackgroundArt("ui_art\\selgame.pcx"); + + return false; +} + void selgame_Diff_Select(int value) { + if (!IsDifficultyAllowed(value)) { + selgame_GameSelection_Select(0); + return; + } + gbDifficulty = value; if (provider == SELCONN_LOOPBACK) { @@ -193,13 +226,18 @@ void selgame_Password_Select(int value) if (selgame_selectedGame) { setIniValue("Phone Book", "Entry1", selgame_Ip); if (SNetJoinGame(selgame_selectedGame, selgame_Ip, selgame_Password, NULL, NULL, gdwPlayerId)) { + if (!IsDifficultyAllowed(m_client_info->initdata->bDiff)) { + selgame_GameSelection_Select(1); + return; + } + UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selgame_endMenu = true; } else { - UiErrorOkDialog( - "Unable to establish a connection.", - PROJECT_NAME " v" PROJECT_VERSION " game not found or password invalid.", - ENTERPASSWORD_DIALOG, size(ENTERPASSWORD_DIALOG)); + selgame_Free(); + BlackPalette(); + UiSelOkDialog("Multi Player Game", SDL_GetError(), false); + LoadBackgroundArt("ui_art\\selgame.pcx"); selgame_Password_Init(selgame_selectedGame); } return; @@ -212,7 +250,10 @@ void selgame_Password_Select(int value) UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selgame_endMenu = true; } else { - UiErrorOkDialog("Unable to create game.", ENTERPASSWORD_DIALOG, size(ENTERPASSWORD_DIALOG)); + selgame_Free(); + BlackPalette(); + UiSelOkDialog("Multi Player Game", SDL_GetError(), false); + LoadBackgroundArt("ui_art\\selgame.pcx"); selgame_Password_Init(0); } } @@ -239,4 +280,4 @@ int UiSelectGame(int a1, _SNETPROGRAMDATA *client_info, _SNETPLAYERDATA *user_in return selgame_enteringGame; } -} +} // namespace dvl diff --git a/SourceX/DiabloUI/selhero.cpp b/SourceX/DiabloUI/selhero.cpp index eb6142387..385b281e8 100644 --- a/SourceX/DiabloUI/selhero.cpp +++ b/SourceX/DiabloUI/selhero.cpp @@ -5,10 +5,12 @@ #include #include "DiabloUI/diabloui.h" -#include "DiabloUI/dialogs.h" +#include "../../DiabloUI/diabloui.h" #include "devilution.h" -#include "scrollbar.h" -#include "selyesno.h" +#include "DiabloUI/dialogs.h" +#include "DiabloUI/scrollbar.h" +#include "DiabloUI/selyesno.h" +#include "DiabloUI/selok.h" namespace dvl { @@ -58,7 +60,7 @@ UiListItem SELLIST_DIALOG_ITEMS[kMaxViewportItems]; UiItem SELLIST_DIALOG[] = { UiArtText("Select Hero", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), UiList(SELLIST_DIALOG_ITEMS, 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD), - MakeScrollBar({585, 244, 25, 178}), + MakeScrollBar({ 585, 244, 25, 178 }), UiArtTextButton("OK", &UiFocusNavigationSelect, { 239, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD), UiArtTextButton("Delete", &selhero_UiFocusNavigationYesNo, { 364, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_DISABLED), UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 489, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) @@ -238,6 +240,15 @@ void selhero_ClassSelector_Focus(int value) void selhero_ClassSelector_Select(int value) { + if (gbSpawned && (value == 1 || value == 2)) { + selhero_Free(); + BlackPalette(); + UiSelOkDialog(nullptr, "The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.", false); + LoadBackgroundArt("ui_art\\selhero.pcx"); + selhero_List_Select(selhero_SaveCount); + return; + } + strcpy(title, "New Single Player Hero"); if (selhero_isMultiPlayer) { strcpy(title, "New Multi Player Hero"); @@ -261,14 +272,24 @@ void selhero_ClassSelector_Esc() void selhero_Name_Select(int value) { - if (gfnHeroCreate(&selhero_heroInfo)) { + if (!UiValidPlayerName(selhero_heroInfo.name)) { + selhero_Free(); + BlackPalette(); + UiSelOkDialog(title, "Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n", false); + LoadBackgroundArt("ui_art\\selhero.pcx"); + } else if (gfnHeroCreate(&selhero_heroInfo)) { UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selhero_endMenu = true; + return; } else { UiErrorOkDialog("Unable to create character.", SELHERO_DIALOG, size(SELHERO_DIALOG)); - memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name)); - selhero_ClassSelector_Select(selhero_heroInfo.heroclass); } + + memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name)); +#ifdef PREFILL_PLAYER_NAME + strcpy(selhero_heroInfo.name, selhero_GenerateName(selhero_heroInfo.heroclass)); +#endif + selhero_ClassSelector_Select(selhero_heroInfo.heroclass); } void selhero_Name_Esc() @@ -336,15 +357,14 @@ BOOL UiSelHeroDialog( } BlackPalette(); selhero_Free(); - if (selhero_navigateYesNo) { - if (!UiSelHeroDelYesNoDialog(gfnHeroDelete, &selhero_heroInfo, selhero_isMultiPlayer)) - app_fatal("Unable to load Yes/No dialog"); + UiSelHeroDelYesNoDialog(gfnHeroDelete, &selhero_heroInfo, selhero_isMultiPlayer); } } while (selhero_navigateYesNo); *dlgresult = selhero_result; strcpy(name, selhero_heroInfo.name); + heroLevel = selhero_heroInfo.level; UnloadScrollBar(); return true; diff --git a/SourceX/DiabloUI/selok.cpp b/SourceX/DiabloUI/selok.cpp new file mode 100644 index 000000000..2023ed49f --- /dev/null +++ b/SourceX/DiabloUI/selok.cpp @@ -0,0 +1,86 @@ +#include "devilution.h" +#include "DiabloUI/diabloui.h" +#include "DiabloUI/text.h" +#include "DiabloUI/selok.h" + +namespace dvl { + +namespace { + +char dialogText[256]; + +} // namespace + +int selok_endMenu; +char selok_title[32]; + +void selok_Free() +{ + ArtBackground.Unload(); +} + +void selok_Select(int value) +{ + selok_endMenu = true; +} + +void selok_Esc() +{ + selok_endMenu = true; +} + +UiListItem SELOK_DIALOG_ITEMS[] = { + { "OK", 0 } +}; + +UiItem SELOK_DIALOG[] = { + MAINMENU_BACKGROUND, + MAINMENU_LOGO, + UiArtText(selok_title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiArtText(dialogText, { 140, 210, 560, 168 }, UIS_MED), + UiList(SELOK_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD) +}; + +UiItem SPAWNERR_DIALOG[] = { + MAINMENU_BACKGROUND, + MAINMENU_LOGO, + UiArtText(dialogText, { 140, 197, 560, 168 }, UIS_MED), + UiList(SELOK_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD) +}; + +void UiSelOkDialog(const char *title, const char *body, bool background) +{ + if (!background) { + LoadBackgroundArt("ui_art\\black.pcx"); + } else { + if (!gbSpawned) { + LoadBackgroundArt("ui_art\\mainmenu.pcx"); + } else { + LoadBackgroundArt("ui_art\\swmmenu.pcx"); + } + } + + UiItem *items = SPAWNERR_DIALOG; + int itemCnt = size(SPAWNERR_DIALOG); + if (title != nullptr) { + strcpy(selok_title, title); + items = SELOK_DIALOG; + itemCnt = size(SELOK_DIALOG); + } + + strcpy(dialogText, body); + WordWrapArtStr(dialogText, 280); + + UiInitList(0, 0, NULL, selok_Select, selok_Esc, items, itemCnt, false, NULL); + + selok_endMenu = false; + while (!selok_endMenu) { + UiRenderItems(items, itemCnt); + UiPollAndRender(); + } + + BlackPalette(); + + selok_Free(); +} +} // namespace dvl diff --git a/SourceX/DiabloUI/selok.h b/SourceX/DiabloUI/selok.h new file mode 100644 index 000000000..05e953588 --- /dev/null +++ b/SourceX/DiabloUI/selok.h @@ -0,0 +1,11 @@ +#pragma once + +#include "devilution.h" + +namespace dvl { +void UiSelOkDialog(const char *title, const char *body, bool background); +void selok_Free(); +void selok_Select(int value); +void selok_Esc(); + +} diff --git a/SourceX/DiabloUI/selyesno.cpp b/SourceX/DiabloUI/selyesno.cpp index 8966a0537..64f63474e 100644 --- a/SourceX/DiabloUI/selyesno.cpp +++ b/SourceX/DiabloUI/selyesno.cpp @@ -45,7 +45,7 @@ void selyesno_Esc() selyesno_endMenu = true; } -BOOL UiSelHeroDelYesNoDialog( +void UiSelHeroDelYesNoDialog( BOOL (*fnremove)(_uiheroinfo *), _uiheroinfo *selectHero, bool isMultiplayer) @@ -74,6 +74,5 @@ BOOL UiSelHeroDelYesNoDialog( BlackPalette(); selyesno_Free(); - return true; } } diff --git a/SourceX/DiabloUI/selyesno.h b/SourceX/DiabloUI/selyesno.h index e1b77fa5c..3147a87dd 100644 --- a/SourceX/DiabloUI/selyesno.h +++ b/SourceX/DiabloUI/selyesno.h @@ -3,7 +3,7 @@ #include "devilution.h" namespace dvl { -BOOL UiSelHeroDelYesNoDialog(BOOL (*fnremove)(_uiheroinfo *), _uiheroinfo *selectHero, bool isMultiplayer); +void UiSelHeroDelYesNoDialog(BOOL (*fnremove)(_uiheroinfo *), _uiheroinfo *selectHero, bool isMultiplayer); void selyesno_Free(); void selyesno_Select(int value); void selyesno_Esc(); diff --git a/SourceX/dvlnet/abstract_net.h b/SourceX/dvlnet/abstract_net.h index 73430c37e..a01fcde86 100644 --- a/SourceX/dvlnet/abstract_net.h +++ b/SourceX/dvlnet/abstract_net.h @@ -13,6 +13,11 @@ namespace net { typedef std::vector buffer_t; typedef unsigned long provider_t; class dvlnet_exception : public std::exception { +public: + const char *what() const throw() override + { + return "Network error"; + } }; class abstract_net { diff --git a/SourceX/dvlnet/frame_queue.h b/SourceX/dvlnet/frame_queue.h index 8c75a0ad3..8e48bfa8b 100644 --- a/SourceX/dvlnet/frame_queue.h +++ b/SourceX/dvlnet/frame_queue.h @@ -8,6 +8,11 @@ namespace dvl { namespace net { class frame_queue_exception : public dvlnet_exception { +public: + const char *what() const throw() override + { + return "Incorrect frame size"; + } }; typedef uint32_t framesize_t; diff --git a/SourceX/dvlnet/packet.h b/SourceX/dvlnet/packet.h index 9eb512aa9..d5584e605 100644 --- a/SourceX/dvlnet/packet.h +++ b/SourceX/dvlnet/packet.h @@ -35,6 +35,11 @@ static constexpr plr_t PLR_MASTER = 0xFE; static constexpr plr_t PLR_BROADCAST = 0xFF; class packet_exception : public dvlnet_exception { +public: + const char *what() const throw() override + { + return "Incorrect package size"; + } }; class packet { @@ -57,7 +62,7 @@ protected: public: packet(const key_t &k) - : key(k) {}; + : key(k) {}; const buffer_t &data(); @@ -152,7 +157,7 @@ void packet_in::process_element(T &x) throw packet_exception(); std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); decrypted_buffer.erase(decrypted_buffer.begin(), - decrypted_buffer.begin() + sizeof(T)); + decrypted_buffer.begin() + sizeof(T)); } template <> @@ -181,7 +186,7 @@ inline void packet_out::create(plr_t s, plr_t d, turn_t u) template <> inline void packet_out::create(plr_t s, plr_t d, - cookie_t c, buffer_t i) + cookie_t c, buffer_t i) { if (have_encrypted || have_decrypted) ABORT(); @@ -195,7 +200,7 @@ inline void packet_out::create(plr_t s, plr_t d, template <> inline void packet_out::create(plr_t s, plr_t d, cookie_t c, - plr_t n, buffer_t i) + plr_t n, buffer_t i) { if (have_encrypted || have_decrypted) ABORT(); @@ -222,7 +227,7 @@ inline void packet_out::create(plr_t s, plr_t d, plr_t n) template <> inline void packet_out::create(plr_t s, plr_t d, plr_t n, - leaveinfo_t l) + leaveinfo_t l) { if (have_encrypted || have_decrypted) ABORT(); diff --git a/SourceX/dvlnet/tcp_client.cpp b/SourceX/dvlnet/tcp_client.cpp index 6edffee38..dac7bcd72 100644 --- a/SourceX/dvlnet/tcp_client.cpp +++ b/SourceX/dvlnet/tcp_client.cpp @@ -17,7 +17,7 @@ int tcp_client::create(std::string addrstr, std::string passwd) local_server.reset(new tcp_server(ioc, addrstr, port, passwd)); return join(local_server->localhost_self(), passwd); } catch (std::system_error &e) { - eprintf("%s\n", e.what()); + SDL_SetError(e.what()); return -1; } } @@ -34,7 +34,7 @@ int tcp_client::join(std::string addrstr, std::string passwd) asio::ip::tcp::no_delay option(true); sock.set_option(option); } catch (std::exception &e) { - eprintf("%s\n", e.what()); + SDL_SetError(e.what()); return -1; } start_recv(); @@ -49,6 +49,7 @@ int tcp_client::join(std::string addrstr, std::string passwd) try { poll(); } catch (const std::runtime_error &e) { + SDL_SetError(e.what()); return -1; } if (plr_self != PLR_BROADCAST) @@ -56,7 +57,12 @@ int tcp_client::join(std::string addrstr, std::string passwd) SDL_Delay(ms_sleep); } } - return (plr_self == PLR_BROADCAST ? -1 : plr_self); + if (plr_self == PLR_BROADCAST) { + SDL_SetError("Unable to connect"); + return -1; + } + + return plr_self; } void tcp_client::poll() diff --git a/SourceX/dvlnet/tcp_server.cpp b/SourceX/dvlnet/tcp_server.cpp index 630e8e71f..86c4c4e87 100644 --- a/SourceX/dvlnet/tcp_server.cpp +++ b/SourceX/dvlnet/tcp_server.cpp @@ -82,6 +82,7 @@ void tcp_server::handle_recv(scc con, const asio::error_code &ec, handle_recv_packet(*pkt); } } catch (dvlnet_exception &e) { + SDL_Log("Network error: %s", e.what()); drop_connection(con); return; } diff --git a/SourceX/dvlnet/tcp_server.h b/SourceX/dvlnet/tcp_server.h index c6bddfde7..2c2140e91 100644 --- a/SourceX/dvlnet/tcp_server.h +++ b/SourceX/dvlnet/tcp_server.h @@ -16,6 +16,11 @@ namespace dvl { namespace net { class server_exception : public dvlnet_exception { +public: + const char *what() const throw() override + { + return "Invalid player ID"; + } }; class tcp_server { diff --git a/SourceX/dvlnet/udp_p2p.cpp b/SourceX/dvlnet/udp_p2p.cpp index f07dbd2b4..db53d23f0 100644 --- a/SourceX/dvlnet/udp_p2p.cpp +++ b/SourceX/dvlnet/udp_p2p.cpp @@ -32,6 +32,7 @@ int udp_p2p::create(std::string addrstr, std::string passwd) try { sock.bind(endpoint(ipaddr, port)); } catch (std::exception &e) { + SDL_SetError(e.what()); return -1; } plr_self = 0; @@ -53,10 +54,10 @@ int udp_p2p::join(std::string addrstr, std::string passwd) master = themaster; { // hack: try to join for 5 seconds randombytes_buf(reinterpret_cast(&cookie_self), - sizeof(cookie_t)); + sizeof(cookie_t)); auto pkt = pktfty->make_packet(PLR_BROADCAST, - PLR_MASTER, cookie_self, - game_init_info); + PLR_MASTER, cookie_self, + game_init_info); send(*pkt); for (auto i = 0; i < 5; ++i) { recv(); @@ -91,10 +92,12 @@ void udp_p2p::recv() auto pkt = pktfty->make_packet(pkt_buf); recv_decrypted(*pkt, sender); } catch (packet_exception &e) { + SDL_Log("Incorrect package size"); // drop packet } } } catch (std::exception &e) { + SDL_Log(e.what()); return; } } @@ -120,7 +123,7 @@ std::set udp_p2p::dests_for_addr(plr_t dest, endpoint sender) if (i != plr_self && connected_table[i]) ret.insert(nexthop_table[i]); ret.insert(connection_requests_pending.begin(), - connection_requests_pending.end()); + connection_requests_pending.end()); } else if (dest == PLR_MASTER) { if (master != none) ret.insert(master); @@ -139,8 +142,8 @@ void udp_p2p::handle_join_request(packet &pkt, endpoint sender) } } auto reply = pktfty->make_packet(plr_self, PLR_BROADCAST, - pkt.cookie(), i, - game_init_info); + pkt.cookie(), i, + game_init_info); send(*reply); }