diff --git a/src/AsyncDownloader.c b/src/AsyncDownloader.c index 5d18b3e40..d1fa32eaa 100644 --- a/src/AsyncDownloader.c +++ b/src/AsyncDownloader.c @@ -6,9 +6,9 @@ #include "GameStructs.h" void ASyncRequest_Free(struct AsyncRequest* request) { - Mem_Free(request->ResultData); - request->ResultData = NULL; - request->ResultSize = 0; + Mem_Free(request->Data); + request->Data = NULL; + request->Size = 0; } #define ASYNC_DEF_ELEMS 10 @@ -205,9 +205,9 @@ static void AsyncDownloader_ProcessRequest(struct AsyncRequest* request) { Platform_Log3("HTTP: return code %i (http %i), in %i ms", &request->Result, &request->StatusCode, &elapsed); - if (request->ResultData) { - size = request->ResultSize; - addr = (uintptr_t)request->ResultData; + if (request->Data) { + size = request->Size; + addr = (uintptr_t)request->Data; Platform_Log2("HTTP returned data: %i bytes at %x", &size, &addr); } } diff --git a/src/AsyncDownloader.h b/src/AsyncDownloader.h index e970049e2..46ef83410 100644 --- a/src/AsyncDownloader.h +++ b/src/AsyncDownloader.h @@ -20,20 +20,20 @@ enum AsyncProgress { }; struct AsyncRequest { - char URL[STRING_SIZE]; - char ID[STRING_SIZE]; + char URL[STRING_SIZE]; /* URL data is downloaded from/uploaded to. */ + char ID[STRING_SIZE]; /* Unique identifier for this request. */ + TimeMS TimeAdded; /* Time this request was added to queue of requests. */ + TimeMS TimeDownloaded; /* Time response contents were completely downloaded. */ + int StatusCode; /* HTTP status code returned in the response. */ + uint32_t ContentLength; /* HTTP content length returned in the response. */ - TimeMS TimeAdded, TimeDownloaded; - int StatusCode; - uint32_t ContentLength; - - ReturnCode Result; - void* ResultData; - uint32_t ResultSize; + ReturnCode Result; /* 0 on success, otherwise platform-specific error. */ + void* Data; /* Contents of the response. */ + uint32_t Size; /* Size of the contents. */ TimeMS LastModified; /* Time item cached at (if at all) */ char Etag[STRING_SIZE]; /* ETag of cached item (if any) */ - uint8_t RequestType; + uint8_t RequestType; /* Whether to fetch contents or just headers. */ }; void ASyncRequest_Free(struct AsyncRequest* request); @@ -42,7 +42,7 @@ void AsyncDownloader_GetSkin(const String* id, const String* skinName); void AsyncDownloader_GetData(const String* url, bool priority, const String* id); void AsyncDownloader_GetContentLength(const String* url, bool priority, const String* id); /* TODO: Implement post */ -/* void AsyncDownloader_PostString(const String* url, bool priority, const String* id, const String* contents); */ +void AsyncDownloader_UNSAFE_PostData(const String* url, bool priority, const String* id, const void* data, const uint32_t size); void AsyncDownloader_GetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag); bool AsyncDownloader_Get(const String* id, struct AsyncRequest* item); diff --git a/src/Entity.c b/src/Entity.c index 4ecc81985..d8d1a1372 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -698,8 +698,8 @@ static void Player_CheckSkin(struct Player* p) { } if (!AsyncDownloader_Get(&skin, &item)) return; - if (!item.ResultData) { Player_SetSkinAll(p, true); return; } - Stream_ReadonlyMemory(&mem, item.ResultData, item.ResultSize); + if (!item.Data) { Player_SetSkinAll(p, true); return; } + Stream_ReadonlyMemory(&mem, item.Data, item.Size); if ((res = Png_Decode(&bmp, &mem))) { url = String_FromRawArray(item.URL); diff --git a/src/LScreens.c b/src/LScreens.c index 0356e7866..eb2a10f5b 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -433,7 +433,7 @@ static void ColoursScreen_KeyDown(struct LScreen* s, Key key) { } } -static void ColoursScreen_ResetAll(void* widget, int x, int y) { +static void ColoursScreen_ResetAll(void* w, int x, int y) { Launcher_ResetSkin(); Launcher_SaveSkin(); ColoursScreen_UpdateAll(&ColoursScreen_Instance); @@ -884,7 +884,7 @@ static void MainScreen_TickFetchServers(struct MainScreen* s) { if (FetchServersTask.Base.Success) { s->SigningIn = false; - Launcher_SetScreen(SettingsScreen_MakeInstance()); + Launcher_SetScreen(ServersScreen_MakeInstance()); } else { MainScreen_Error(&FetchServersTask.Base, "retrieving servers list"); } @@ -898,7 +898,6 @@ static void MainScreen_Tick(struct LScreen* s_) { MainScreen_TickFetchServers(s); } - struct LScreen* MainScreen_MakeInstance(void) { struct MainScreen* s = &MainScreen_Instance; LScreen_Reset((struct LScreen*)s); @@ -911,6 +910,68 @@ struct LScreen* MainScreen_MakeInstance(void) { } +/*########################################################################################################################* +*--------------------------------------------------------ServersScreen----------------------------------------------------* +*#########################################################################################################################*/ +static struct ServersScreen { + LScreen_Layout + struct LInput IptName, IptHash; + struct LButton BtnBack, BtnConnect, BtnRefresh; + struct LWidget* _widgets[5]; +} ServersScreen_Instance; + +static void ServersScreen_Connect(void* w, int x, int y) { + Launcher_ConnectToServer(&ServersScreen_Instance.IptHash.Text); +} + +static void ServersScreen_Refresh(void* w, int x, int y) { + const static String working = String_FromConst("&eWorking.."); + struct LButton* btn; + if (FetchServersTask.Base.Working) return; + + FetchServersTask_Run(); + btn = &ServersScreen_Instance.BtnRefresh; + LButton_SetText(btn, &working); + LWidget_Redraw(btn); +} + +static void ServersScreen_Init(struct LScreen* s_) { + struct ServersScreen* s = (struct ServersScreen*)s_; + if (s->NumWidgets) return; + s->Widgets = s->_widgets; + + LScreen_Input(s_, &s->IptName, 370, false, "&gSearch servers.."); + LScreen_Input(s_, &s->IptHash, 475, false, "&gclassicube.net/server/play/..."); + + LScreen_Button(s_, &s->BtnBack, 110, 30, "Back"); + LScreen_Button(s_, &s->BtnConnect, 110, 30, "Connect"); + LScreen_Button(s_, &s->BtnRefresh, 110, 30, "Refresh"); + + s->BtnBack.OnClick = SwitchToMain; + s->BtnConnect.OnClick = ServersScreen_Connect; + s->BtnRefresh.OnClick = ServersScreen_Refresh; +} + +static void ServersScreen_Reposition(struct LScreen* s_) { + struct ServersScreen* s = (struct ServersScreen*)s_; + LWidget_SetLocation(&s->IptName, ANCHOR_MIN, ANCHOR_MIN, 10, 10); + LWidget_SetLocation(&s->IptHash, ANCHOR_MIN, ANCHOR_MAX, 10, 10); + + LWidget_SetLocation(&s->BtnBack, ANCHOR_MAX, ANCHOR_MIN, 10, 10); + LWidget_SetLocation(&s->BtnConnect, ANCHOR_MAX, ANCHOR_MAX, 10, 10); + LWidget_SetLocation(&s->BtnRefresh, ANCHOR_MAX, ANCHOR_MIN, 135, 10); +} + +struct LScreen* ServersScreen_MakeInstance(void) { + struct ServersScreen* s = &ServersScreen_Instance; + LScreen_Reset((struct LScreen*)s); + s->HidesBackground = true; + s->Init = ServersScreen_Init; + s->Reposition = ServersScreen_Reposition; + return (struct LScreen*)s; +} + + /*########################################################################################################################* *--------------------------------------------------------SettingsScreen---------------------------------------------------* *#########################################################################################################################*/ diff --git a/src/LScreens.h b/src/LScreens.h index df5145e02..39de5601c 100644 --- a/src/LScreens.h +++ b/src/LScreens.h @@ -33,7 +33,8 @@ typedef void(*LWidget_Func)(struct LScreen* s, struct LWidget* w); struct LWidget* HoveredWidget; /* Widget the mouse is currently hovering over. */ \ struct LWidget* SelectedWidget; /* Widget mouse last clicked on. */ \ int NumWidgets; /* Number of widgets actually used. */ \ - struct LWidget** Widgets; /* Array of pointers to all widgets in the screen. */ + struct LWidget** Widgets; /* Array of pointers to all widgets in the screen. */ \ + bool HidesBackground; /* Whether titlebar in window is hidden. */ struct LScreen { LScreen_Layout }; diff --git a/src/LWeb.c b/src/LWeb.c index e8352ec08..e435e555d 100644 --- a/src/LWeb.c +++ b/src/LWeb.c @@ -218,8 +218,8 @@ void LWebTask_Tick(struct LWebTask* task) { task->Working = false; task->Completed = true; - task->Success = !task->Res && req.ResultData && req.ResultSize; - if (task->Success) task->Handle(req.ResultData, req.ResultSize); + task->Success = !task->Res && req.Data && req.Size; + if (task->Success) task->Handle(req.Data, req.Size); ASyncRequest_Free(&req); } @@ -261,7 +261,33 @@ void GetTokenTask_Run(void) { *--------------------------------------------------------SignInTask-------------------------------------------------------* *#########################################################################################################################*/ struct SignInTaskData SignInTask; -void SignInTask_Run(const String* user, const String* pass); +char userBuffer[STRING_SIZE]; + +static void SignInTask_OnValue(struct JsonContext* ctx, const String* str) { + //if (!String_CaselessEqualsConst(&ctx->CurKey, "token")) return; + //String_Copy(&GetTokenTask.Token, str); +} + +static void SignInTask_Handle(uint8_t* data, uint32_t len) { + //Json_Handle(data, len, GetTokenTask_OnValue, NULL, NULL); +} + +void SignInTask_Run(const String* user, const String* pass) { + const static String id = String_FromConst("CC post login"); + const static String url = String_FromConst("https://www.classicube.net/api/login"); + if (SignInTask.Base.Working) return; + + LWebTask_Reset(&SignInTask.Base); + String_InitArray(SignInTask.Username, userBuffer); + SignInTask.Error.length = 0; + + // remove this + String_Copy(&SignInTask.Username, user); + + SignInTask.Base.Identifier = id; + AsyncDownloader_GetData(&url, false, &id); + SignInTask.Base.Handle = SignInTask_Handle; +} // NOTE: Remember to add &c for errors here too diff --git a/src/LWidgets.c b/src/LWidgets.c index ca79593d4..709fb0858 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -6,6 +6,7 @@ #include "ExtMath.h" #include "Window.h" #include "Funcs.h" +#include "LWeb.h" #define BORDER 1 @@ -550,3 +551,501 @@ void LSlider_Init(struct LSlider* w, int width, int height) { w->Width = width; w->Height = height; w->MaxValue = 100; } + + +/*########################################################################################################################* +*------------------------------------------------------TableWidget--------------------------------------------------------* +*#########################################################################################################################*/ +static void NameColumn_Get(struct ServerInfo* row, String* str) { + String_Copy(str, &row->Name); +} +static int NameColumn_Sort(struct ServerInfo* a, struct ServerInfo* b) { + return String_Compare(&a->Name, &b->Name); +} + +static void PlayersColumn_Get(struct ServerInfo* row, String* str) { + String_Format2(str, "%i/%i", &row->Players, &row->MaxPlayers); +} +static int PlayersColumn_Sort(struct ServerInfo* a, struct ServerInfo* b) { + return b->Players - a->Players; +} + +static void UptimeColumn_Get(struct ServerInfo* row, String* str) { + int uptime = row->Uptime; + char unit = 's'; + + if (uptime >= SECS_PER_DAY * 7) { + uptime /= SECS_PER_DAY; unit = 'd'; + } else if (uptime >= SECS_PER_HOUR) { + uptime /= SECS_PER_HOUR; unit = 'h'; + } else if (uptime >= SECS_PER_MIN) { + uptime /= SECS_PER_MIN; unit = 'm'; + } + String_Format2(str, "%i%r", &uptime, &unit); +} +static int UptimeColumn_Sort(struct ServerInfo* a, struct ServerInfo* b) { + return b->Uptime - a->Uptime; +} + +static void SoftwareColumn_Get(struct ServerInfo* row, String* str) { + String_Copy(str, &row->Software); +} +static int SoftwareColumn_Sort(struct ServerInfo* a, struct ServerInfo* b) { + return String_Compare(&a->Software, &b->Software); +} + +static struct LTableColumn tableColumns[4] = { + { "Name", 320, 320, NameColumn_Get, NameColumn_Sort }, + { "Players", 65, 65, PlayersColumn_Get, PlayersColumn_Sort }, + { "Uptime", 65, 65, UptimeColumn_Get, UptimeColumn_Sort }, + { "Software", 140, 140, SoftwareColumn_Get, SoftwareColumn_Sort } +}; + +/* +PackedCol backGridCol = new PackedCol(20, 20, 10); +PackedCol foreGridCol = new PackedCol(40, 40, 40); + +int entryHeight, headerHeight; +int headerStartY, headerEndY; +int numEntries, maxIndex; +Font font, titleFont; + +void SetDrawData(IDrawer2D drawer, Font font, Font titleFont) { + this.font = font; + this.titleFont = titleFont; + + headerHeight = drawer.FontHeight(titleFont, true); + entryHeight = drawer.FontHeight(font, true); +} + +void RecalculateDrawData() { + for (int i = 0; i < table.ColumnWidths.Length; i++) { + table.ColumnWidths[i] = table.DesiredColumnWidths[i]; + Utils.Clamp(ref table.ColumnWidths[i], 20, game.Width - 20); + } + table.Width = game.Width - table.X; + ResetEntries(); + + int y = table.Y + 3; + y += headerHeight + 2; + maxIndex = table.Count; + y += 5; + + for (int i = table.CurrentIndex; i < table.Count; i++) { + if (y + entryHeight > table.Y + table.Height) { + maxIndex = i; return; + } + + table.entries[table.order[i]].Y = y; + table.entries[table.order[i]].Height = entryHeight; + y += entryHeight + 2; + } + } + + const int flagPadding = 15; + void RedrawData(IDrawer2D drawer) { + DrawGrid(drawer); + int x = table.X + flagPadding + 5; + x += DrawColumn(drawer, "Name", 0, x, filterName) + 5; + x += DrawColumn(drawer, "Players", 1, x, filterPlayers) + 5; + x += DrawColumn(drawer, "Uptime", 2, x, filterUptime) + 5; + x += DrawColumn(drawer, "Software", 3, x, filterSoftware) + 5; + + DrawScrollbar(drawer); + DrawFlags(); + } + + void Redraw(IDrawer2D drawer) { + RecalculateDrawData(); + RedrawData(drawer); + } + + delegate string ColumnFilter(TableEntry entry); + // cache to avoid allocations every redraw + static string FilterName(TableEntry e) { return e.Name; } static ColumnFilter filterName = FilterName; + static string FilterPlayers(TableEntry e) { return e.Players; } static ColumnFilter filterPlayers = FilterPlayers; + static string FilterUptime(TableEntry e) { return e.Uptime; } static ColumnFilter filterUptime = FilterUptime; + static string FilterSoftware(TableEntry e) { return e.Software; } static ColumnFilter filterSoftware = FilterSoftware; + + static FastBitmap GetFlag(string flag) { + List flags = FetchFlagsTask.Flags; + List bitmaps = FetchFlagsTask.Bitmaps; + + for (int i = 0; i < flags.Count; i++) { + if (flags[i] != flag) continue; + return i < bitmaps.Count ? bitmaps[i] : null; + } + return null; + } + + public void DrawFlags() { + using (FastBitmap dst = game.LockBits()) { + for (int i = table.CurrentIndex; i < maxIndex; i++) { + TableEntry entry = table.Get(i); + FastBitmap flag = GetFlag(entry.Flag); + if (flag == null) continue; + + int x = table.X, y = entry.Y; + Rectangle rect = new Rectangle(x + 2, y + 3, 16, 11); + BitmapDrawer.Draw(flag, dst, rect); + } + } + } + + int DrawColumn(IDrawer2D drawer, string header, int columnI, int x, ColumnFilter filter) { + int y = table.Y + 3; + int maxWidth = table.ColumnWidths[columnI]; + bool separator = columnI > 0; + + DrawTextArgs args = new DrawTextArgs(header, titleFont, true); + TableEntry headerEntry = default(TableEntry); + DrawColumnEntry(drawer, ref args, maxWidth, x, ref y, ref headerEntry); + maxIndex = table.Count; + + y += 5; + for (int i = table.CurrentIndex; i < table.Count; i++) { + TableEntry entry = table.Get(i); + args = new DrawTextArgs(filter(entry), font, true); + + if ((i == table.SelectedIndex || entry.Featured) && !separator) { + int startY = y - 3; + int height = Math.Min(startY + (entryHeight + 4), table.Y + table.Height) - startY; + drawer.Clear(GetGridCol(entry.Featured, i == table.SelectedIndex), table.X, startY, table.Width, height); + } + if (!DrawColumnEntry(drawer, ref args, maxWidth, x, ref y, ref entry)) { + maxIndex = i; break; + } + } + if (separator && !game.ClassicBackground) { + drawer.Clear(LauncherSkin.BackgroundCol, x - 7, table.Y, 2, table.Height); + } + return maxWidth + 5; + } + + PackedCol GetGridCol(bool featured, bool selected) { + if (featured) { + if (selected) return new PackedCol(50, 53, 0); + return new PackedCol(101, 107, 0); + } + return foreGridCol; + } + + bool DrawColumnEntry(IDrawer2D drawer, ref DrawTextArgs args, + int maxWidth, int x, ref int y, ref TableEntry entry) { + Size size = drawer.MeasureText(ref args); + bool empty = args.Text == ""; + if (empty) + size.Height = entryHeight; + if (y + size.Height > table.Y + table.Height) { + y = table.Y + table.Height + 2; return false; + } + + entry.Y = y; entry.Height = size.Height; + if (!empty) { + size.Width = Math.Min(maxWidth, size.Width); + args.SkipPartsCheck = false; + Drawer2DExt.DrawClippedText(ref args, drawer, x, y, maxWidth); + } + y += size.Height + 2; + return true; + } + + void ResetEntries() { + for (int i = 0; i < table.Count; i++) { + table.entries[i].Height = 0; + table.entries[i].Y = -10; + } + } + + void DrawGrid(IDrawer2D drawer) { + if (!game.ClassicBackground) + drawer.Clear(LauncherSkin.BackgroundCol, + table.X, table.Y + headerHeight + 5, table.Width, 2); + headerStartY = table.Y; + + headerEndY = table.Y + headerHeight + 5; + int startY = headerEndY + 3; + numEntries = (table.Y + table.Height - startY) / (entryHeight + 3); + } + + void DrawScrollbar(IDrawer2D drawer) { + PackedCol col = game.ClassicBackground ? new PackedCol(80, 80, 80) : LauncherSkin.ButtonBorderCol; + drawer.Clear(col, game.Width - 10, table.Y, 10, table.Height); + col = game.ClassicBackground ? new PackedCol(160, 160, 160) : LauncherSkin.ButtonForeActiveCol; + int yOffset, height; + table.GetScrollbarCoords(out yOffset, out height); + drawer.Clear(col, game.Width - 10, table.Y + yOffset, 10, height); + } +} +} + +internal struct TableEntry { + public string Hash, Name, Players, Uptime, Software, RawUptime, Flag; + public int Y, Height; + public bool Featured; +} + +public delegate void TableNeedsRedrawHandler(); + + TableView view; + public TableWidget(LauncherWindow window) : base(window) { + OnClick = HandleOnClick; + view = new TableView(); + view.Init(window, this); + } + + public TableNeedsRedrawHandler NeedRedraw; + public Action SelectedChanged; + public int SelectedIndex = -1; + public string SelectedHash = ""; + public int CurrentIndex, Count; + + internal TableEntry[] entries; + internal int[] order; + internal List servers; + internal TableEntry Get(int i) { return entries[order[i]]; } + + public void SetEntries(List servers) { + entries = new TableEntry[servers.Count]; + order = new int[servers.Count]; + this.servers = servers; + + for (int i = 0; i < servers.Count; i++) { + ServerListEntry e = servers[i]; + TableEntry tableEntry = default(TableEntry); + tableEntry.Hash = e.Hash; + tableEntry.Name = e.Name; + tableEntry.Players = e.Players + "/" + e.MaxPlayers; + tableEntry.Software = e.Software; + tableEntry.Uptime = MakeUptime(e.Uptime); + tableEntry.RawUptime = e.Uptime; + tableEntry.Featured = e.Featured; + tableEntry.Flag = e.Flag; + + entries[i] = tableEntry; + order[i] = i; + } + Count = entries.Length; + } + + string curFilter; + public void FilterEntries(string filter) { + curFilter = filter; + Count = 0; + for (int i = 0; i < entries.Length; i++) { + TableEntry entry = entries[i]; + if (entry.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0) { + order[Count++] = i; + } + } + for (int i = Count; i < entries.Length; i++) { + order[i] = -100000; + } + } + + internal void GetScrollbarCoords(out int y, out int height) { + if (Count == 0) { y = 0; height = 0; return; } + + float scale = Height / (float)Count; + y = (int)Math.Ceiling(CurrentIndex * scale); + height = (int)Math.Ceiling((view.maxIndex - CurrentIndex) * scale); + height = Math.Min(y + height, Height) - y; + } + + public void SetSelected(int index) { + if (index >= view.maxIndex) CurrentIndex = index + 1 - view.numEntries; + if (index < CurrentIndex) CurrentIndex = index; + if (index >= Count) index = Count - 1; + if (index < 0) index = 0; + + SelectedHash = ""; + SelectedIndex = index; + lastIndex = index; + ClampIndex(); + + if (Count > 0) { + TableEntry entry = Get(index); + SelectedChanged(entry.Hash); + SelectedHash = entry.Hash; + } + } + + public void SetSelected(string hash) { + SelectedIndex = -1; + for (int i = 0; i < Count; i++) { + if (Get(i).Hash != hash) continue; + SetSelected(i); + return; + } + } + + public void ClampIndex() { + if (CurrentIndex > Count - view.numEntries) + CurrentIndex = Count - view.numEntries; + if (CurrentIndex < 0) + CurrentIndex = 0; + } + + string MakeUptime(string rawSeconds) { + TimeSpan t = TimeSpan.FromSeconds(Double.Parse(rawSeconds)); + if (t.TotalHours >= 24 * 7) + return (int)t.TotalDays + "d"; + if (t.TotalHours >= 1) + return (int)t.TotalHours + "h"; + if (t.TotalMinutes >= 1) + return (int)t.TotalMinutes + "m"; + return (int)t.TotalSeconds + "s"; + } + + + public int[] ColumnWidths = new int[] { 320, 65, 65, 140 }; + public int[] DesiredColumnWidths = new int[] { 320, 65, 65, 140 }; + + public void SetDrawData(IDrawer2D drawer, Font font, Font titleFont, + Anchor horAnchor, Anchor verAnchor, int x, int y) { + SetLocation(horAnchor, verAnchor, x, y); + view.SetDrawData(drawer, font, titleFont); + } + + public void RecalculateDrawData() { view.RecalculateDrawData(); } + public void RedrawData(IDrawer2D drawer) { view.RedrawData(drawer); } + public void RedrawFlags() { view.DrawFlags(); } + + public override void Redraw(IDrawer2D drawer) { + RecalculateDrawData(); + RedrawData(drawer); + } + + + DefaultComparer defComp = new DefaultComparer(); + NameComparer nameComp = new NameComparer(); + PlayersComparer playerComp = new PlayersComparer(); + UptimeComparer uptimeComp = new UptimeComparer(); + SoftwareComparer softwareComp = new SoftwareComparer(); + internal int DraggingColumn = -1; + internal bool DraggingScrollbar = false; + internal int mouseOffset; + + public void SortDefault() { + SortEntries(defComp, true); + } + + void SelectHeader(int mouseX, int mouseY) { + int x = X + 15; + for (int i = 0; i < ColumnWidths.Length; i++) { + x += ColumnWidths[i] + 10; + if (mouseX >= x - 8 && mouseX < x + 8) { + DraggingColumn = i; + lastIndex = -10; return; + } + } + TrySortColumns(mouseX); + } + + void TrySortColumns(int mouseX) { + int x = X + TableView.flagPadding; + if (mouseX >= x && mouseX < x + ColumnWidths[0]) { + SortEntries(nameComp, false); return; + } + + x += ColumnWidths[0] + 10; + if (mouseX >= x && mouseX < x + ColumnWidths[1]) { + SortEntries(playerComp, false); return; + } + + x += ColumnWidths[1] + 10; + if (mouseX >= x && mouseX < x + ColumnWidths[2]) { + SortEntries(uptimeComp, false); return; + } + + x += ColumnWidths[2] + 10; + if (mouseX >= x) { + SortEntries(softwareComp, false); return; + } + } + + void SortEntries(TableEntryComparer comparer, bool noRedraw) { + Array.Sort(entries, 0, entries.Length, comparer); + lastIndex = -10; + if (curFilter != null && curFilter.Length > 0) { + FilterEntries(curFilter); + } + + if (noRedraw) return; + comparer.Invert = !comparer.Invert; + SetSelected(SelectedHash); + NeedRedraw(); + } + + void GetSelectedServer(int mouseX, int mouseY) { + for (int i = 0; i < Count; i++) { + TableEntry entry = Get(i); + if (mouseY < entry.Y || mouseY >= entry.Y + entry.Height + 2) continue; + + if (lastIndex == i && (DateTime.UtcNow - lastPress).TotalSeconds < 1) { + Window.ConnectToServer(servers, entry.Hash); + lastPress = DateTime.MinValue; + return; + } + + SetSelected(i); + NeedRedraw(); + break; + } + } + + void HandleOnClick(int mouseX, int mouseY) { + if (mouseX >= Window.Width - 10) { + ScrollbarClick(mouseY); + DraggingScrollbar = true; + lastIndex = -10; return; + } + + if (mouseY >= view.headerStartY && mouseY < view.headerEndY) { + SelectHeader(mouseX, mouseY); + } else { + GetSelectedServer(mouseX, mouseY); + } + lastPress = DateTime.UtcNow; + } + + int lastIndex = -10; + DateTime lastPress; + public void MouseMove(int x, int y, int deltaX, int deltaY) { + if (DraggingScrollbar) { + y -= Y; + float scale = Height / (float)Count; + CurrentIndex = (int)((y - mouseOffset) / scale); + ClampIndex(); + NeedRedraw(); + } else if (DraggingColumn >= 0) { + if (x >= Window.Width - 20) return; + int col = DraggingColumn; + ColumnWidths[col] += deltaX; + Utils.Clamp(ref ColumnWidths[col], 20, Window.Width - 20); + DesiredColumnWidths[col] = ColumnWidths[col]; + NeedRedraw(); + } + + } + + void ScrollbarClick(int mouseY) { + mouseY -= Y; + int y, height; + GetScrollbarCoords(out y, out height); + int delta = (view.maxIndex - CurrentIndex); + + if (mouseY < y) { + CurrentIndex -= delta; + } else if (mouseY >= y + height) { + CurrentIndex += delta; + } else { + DraggingScrollbar = true; + mouseOffset = mouseY - y; + } + ClampIndex(); + NeedRedraw(); + } +} +} +*/ \ No newline at end of file diff --git a/src/LWidgets.h b/src/LWidgets.h index 4fd16ea65..8746734f8 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -111,4 +111,22 @@ struct LSlider { BitmapCol ProgressCol; }; CC_NOINLINE void LSlider_Init(struct LSlider* w, int width, int height); + +struct ServerInfo; +struct LTableColumn { + /* Name of this column. */ + const char* Name; + /* Current and default width of this column. */ + int Width, DefaultWidth; + /* Gets the value of this column for the given row. */ + void (*GetValue)(struct ServerInfo* row, String* str); + /* Sorts two rows based on value of this column in both rows. */ + int (*SortRows)(struct ServerInfo* a, struct ServerInfo* b); + /* Whether to invert the order of row sorting. */ + bool InvertSort; +}; +/* Represents a table of server entries. */ +struct LTable { + LWidget_Layout +}; #endif diff --git a/src/Launcher.c b/src/Launcher.c index c4180f61e..3eaf7c725 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -14,12 +14,9 @@ #include "AsyncDownloader.h" /* TODO TODO TODO TODO TODO TODO TODO TODO FIX THESE STUBS */ -void SignInTask_Run(const String* user, const String* pass) { } void Launcher_SetSecureOpt(const char* opt, const String* data, const String* key) { } void Launcher_GetSecureOpt(const char* opt, String* data, const String* key) { } struct LScreen* ResourcesScreen_MakeInstance(void) { return NULL; } -struct LScreen* ServersScreen_MakeInstance(void) { return NULL; } - struct LScreen* Launcher_Screen; bool Launcher_Dirty; Rect2D Launcher_DirtyArea; @@ -55,6 +52,50 @@ void Launcher_SetScreen(struct LScreen* screen) { Launcher_Redraw(); } +CC_NOINLINE static void Launcher_StartFromInfo(struct ServerInfo* info) { + String port; char portBuffer[STRING_INT_CHARS]; + String_InitArray(port, portBuffer); + + String_AppendInt(&port, info->Port); + Launcher_StartGame(&SignInTask.Username, &info->Mppass, &info->IP, &port, &info->Name); +} + +bool Launcher_ConnectToServer(const String* hash) { + int i; + struct ServerInfo* info; + if (!hash->length) return false; + + for (i = 0; i < FetchServersTask.NumServers; i++) { + info = &FetchServersTask.Servers[i]; + if (!String_Equals(hash, &info->Hash)) continue; + + Launcher_StartFromInfo(info); + return true; + } + + /* Fallback to private server handling */ + /* TODO: Rewrite to be async */ + FetchServerTask_Run(hash); + + while (!FetchServerTask.Base.Completed) { + LWebTask_Tick(&FetchServerTask.Base); + Thread_Sleep(10); + } + + if (FetchServerTask.Server.Hash.length) { + Launcher_StartFromInfo(&FetchServerTask.Server); + return true; + } else if (FetchServerTask.Base.Res) { + Launcher_ShowError(FetchServerTask.Base.Res, "fetching server info"); + } else if (FetchServerTask.Base.Status != 200) { + /* TODO: Use a better dialog message.. */ + Launcher_ShowError(FetchServerTask.Base.Status, "fetching server info"); + } else { + Window_ShowDialog("Failed to connect", "No server has that hash"); + } + return true; +} + /*########################################################################################################################* *---------------------------------------------------------Event handler---------------------------------------------------* @@ -425,6 +466,11 @@ void Launcher_ResetPixels(void) { struct DrawTextArgs args; int x; + if (Launcher_Screen && Launcher_Screen->HidesBackground) { + Launcher_ResetArea(0, 0, Game_Width, Game_Height); + return; + } + if (Launcher_ClassicBackground && terrainBmp.Scan0) { Launcher_ClearTile(0, 0, Game_Width, TILESIZE, TILESIZE); Launcher_ClearTile(0, TILESIZE, Game_Width, Game_Height - TILESIZE, 0); diff --git a/src/PacketHandlers.c b/src/PacketHandlers.c index 392875c88..c780c8fe4 100644 --- a/src/PacketHandlers.c +++ b/src/PacketHandlers.c @@ -276,7 +276,7 @@ static void WoM_ParseConfig(struct AsyncRequest* item) { PackedCol col; String_InitArray(line, lineBuffer); - Stream_ReadonlyMemory(&mem, item->ResultData, item->ResultSize); + Stream_ReadonlyMemory(&mem, item->Data, item->Size); while (!Stream_ReadLine(&mem, &line)) { Platform_Log(&line); @@ -311,7 +311,7 @@ static void WoM_Tick(void) { struct AsyncRequest item; if (!AsyncDownloader_Get(&wom_identifier, &item)) return; - if (item.ResultData) WoM_ParseConfig(&item); + if (item.Data) WoM_ParseConfig(&item); ASyncRequest_Free(&item); } diff --git a/src/Platform.c b/src/Platform.c index 7bfd13398..1042a66ed 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -1384,8 +1384,8 @@ static ReturnCode Http_GetData(struct AsyncRequest* req, HINTERNET handle, volat buffer = Mem_Alloc(size, 1, "http get data"); totalRead = 0; - req->ResultData = buffer; - req->ResultSize = 0; + req->Data = buffer; + req->Size = 0; for (;;) { if (!InternetQueryDataAvailable(handle, &avail, 0, 0)) break; @@ -1395,7 +1395,7 @@ static ReturnCode Http_GetData(struct AsyncRequest* req, HINTERNET handle, volat if (totalRead + avail > size) { size = totalRead + avail; buffer = Mem_Realloc(buffer, size, 1, "http inc data"); - req->ResultData = buffer; + req->Data = buffer; } success = InternetReadFile(handle, &buffer[totalRead], avail, &read); @@ -1404,7 +1404,7 @@ static ReturnCode Http_GetData(struct AsyncRequest* req, HINTERNET handle, volat totalRead += read; if (req->ContentLength) *progress = (int)(100.0f * totalRead / size); - req->ResultSize += read; + req->Size += read; } *progress = 100; diff --git a/src/ServerConnection.c b/src/ServerConnection.c index 0aff334d2..b4fd7fa55 100644 --- a/src/ServerConnection.c +++ b/src/ServerConnection.c @@ -82,7 +82,7 @@ void ServerConnection_CheckAsyncResources(void) { struct AsyncRequest item; if (!AsyncDownloader_Get(&texPack, &item)) return; - if (item.ResultData) { + if (item.Data) { TexturePack_Extract_Req(&item); } else if (item.Result) { Chat_Add1("&cError %i when trying to download texture pack", &item.Result); diff --git a/src/TexturePack.c b/src/TexturePack.c index 11ccbc945..90262b15b 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -719,8 +719,8 @@ void TexturePack_Extract_Req(struct AsyncRequest* item) { url = String_FromRawArray(item->URL); String_Copy(&World_TextureUrl, &url); - data = item->ResultData; - len = item->ResultSize; + data = item->Data; + len = item->Size; etag = String_FromRawArray(item->Etag); TextureCache_Set(&url, data, len);