diff --git a/DataCollection/gamedata.json b/DataCollection/gamedata.json
index dee5886..89a22a9 100644
--- a/DataCollection/gamedata.json
+++ b/DataCollection/gamedata.json
@@ -91,6 +91,15 @@
"end_of_meta":"^Z",
"back_to_map":"^M",
"long_full_line":"^L",
+ "highscores":{
+ "header_meta":"^ATMini-Game Rankings^H",
+ "highscore_format":"%GAMETITLE% Rank: #%RANKING% With: %SCORE% points (%TOTALPLAYS% plays)
",
+ "besttime_format":"%GAMETITLE% Rank: #%RANKING% With: %SCORE% time score (%TOTALPLAYS% plays)
",
+ "game_highscore_header":"Top 20 High Scores for %GAMETITLE%
",
+ "game_highscore_format":"#%RANKING%: %SCORE% points -- %USERNAME% [played %TOTALPLAYS% times]
",
+ "game_besttime_header":"Top 20 Best Times for %GAMETITLE%
",
+ "game_besttime_format":"#%RANKING% Best Time: %SCORE% sec -- %USERNAME% [played %TOTALPLAYS% times]
"
+ },
"quest_log":{
"header_meta":"^ATYour Horse Isle Adventure Log^H",
"quest_format":"%TITLE% (%QUESTPOINTS%qp) [%DIFFICULTY%] %COMPLETION%
",
diff --git a/Horse Isle Server/Horse Isle Server/Game/Messages.cs b/Horse Isle Server/Horse Isle Server/Game/Messages.cs
index e2c8bb0..045b5da 100644
--- a/Horse Isle Server/Horse Isle Server/Game/Messages.cs
+++ b/Horse Isle Server/Horse Isle Server/Game/Messages.cs
@@ -163,6 +163,17 @@ namespace HISP.Game
public static string SellButton;
public static string SellAllButton;
+ // Highscore List
+ public static string HighscoreHeaderMeta;
+ public static string HighscoreFormat;
+ public static string BestTimeFormat;
+
+ public static string GameBestTimeFormat;
+ public static string GameBestTimeHeaderFormat;
+ public static string GameHighScoreHeaderFormat;
+ public static string GameHighScoreFormat;
+
+
// Shop
public static string ThingsIAmSelling;
public static string ThingsYouSellMe;
@@ -229,6 +240,30 @@ namespace HISP.Game
// Click
public static string NothingInterestingHere;
+ public static string FormatBestTimeHeader(string gameName)
+ {
+ return GameBestTimeHeaderFormat.Replace("%GAMETITLE%", gameName);
+ }
+ public static string FormatBestTimeListEntry(int ranking, int score, string username, int totalplays)
+ {
+ return GameBestTimeFormat.Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString().Insert(score.ToString().Length - 2, ".")).Replace("%USERNAME%", username).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
+ }
+ public static string FormatHighscoreHeader(string gameName)
+ {
+ return GameHighScoreHeaderFormat.Replace("%GAMETITLE%", gameName);
+ }
+ public static string FormatHighscoreListEntry(int ranking, int score, string username, int totalplays)
+ {
+ return GameHighScoreFormat.Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString("N0")).Replace("%USERNAME%", username).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
+ }
+ public static string FormatHighscoreStat(string gameTitle, int ranking, int score, int totalplays)
+ {
+ return HighscoreFormat.Replace("%GAMETITLE%", gameTitle).Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString("N0")).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
+ }
+ public static string FormatBestTimeStat(string gameTitle, int ranking, int score, int totalplays)
+ {
+ return BestTimeFormat.Replace("%GAMETITLE%", gameTitle).Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString()).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
+ }
public static string FormatMoneyEarnedMessage(int money)
{
return YouEarnedMoneyFormat.Replace("%MONEY%", money.ToString("N0"));
diff --git a/Horse Isle Server/Horse Isle Server/Game/Meta.cs b/Horse Isle Server/Horse Isle Server/Game/Meta.cs
index dae229a..e0e6ea4 100644
--- a/Horse Isle Server/Horse Isle Server/Game/Meta.cs
+++ b/Horse Isle Server/Horse Isle Server/Game/Meta.cs
@@ -225,7 +225,6 @@ namespace HISP.Game
return message;
}
-
public static string SelectPlayerStatFormat(int statValue)
{
int curValue = 1000;
@@ -240,7 +239,55 @@ namespace HISP.Game
}
throw new Exception("A mathematically impossible error occured. please check wether the laws of physics still apply.");
}
-
+
+ public static string BuildTopHighscores(string gameName)
+ {
+ Highscore.HighscoreTableEntry[] scores = Database.GetTopScores(gameName, 20);
+ if (scores.Length <= 0)
+ return "No scores recorded.";
+ string message = "";
+
+ message += Messages.FormatHighscoreHeader(gameName);
+
+ for (int i = 0; i < scores.Length; i++)
+ {
+ message += Messages.FormatHighscoreListEntry(i+1, scores[i].Score, Database.GetUsername(scores[i].UserId), scores[i].TimesPlayed);
+ }
+ message += Messages.BackToMap;
+ message += Messages.MetaTerminator;
+ return message;
+ }
+ public static string BuildTopTimes(string gameName)
+ {
+ Highscore.HighscoreTableEntry[] scores = Database.GetTopScores(gameName, 20);
+ if (scores.Length <= 0)
+ return "No times recorded.";
+ string message = "";
+
+ message += Messages.FormatBestTimeHeader(gameName);
+
+ for (int i = 0; i < scores.Length; i++)
+ {
+ message += Messages.FormatBestTimeListEntry(i+1, scores[i].Score, Database.GetUsername(scores[i].UserId), scores[i].TimesPlayed);
+ }
+ message += Messages.BackToMap;
+ message += Messages.MetaTerminator;
+ return message;
+ }
+ public static string BuildMinigameRankingsForUser(User user)
+ {
+ string message = Messages.HighscoreHeaderMeta;
+ foreach(Highscore.HighscoreTableEntry highscore in user.Highscores.HighscoreList)
+ {
+ if (highscore.Type == "SCORE")
+ message += Messages.FormatHighscoreStat(highscore.GameName, Database.GetRanking(highscore.Score, highscore.GameName), highscore.Score, highscore.TimesPlayed);
+ else if(highscore.Type == "TIME")
+ message += Messages.FormatBestTimeStat(highscore.GameName, Database.GetRanking(highscore.Score, highscore.GameName), highscore.Score, highscore.TimesPlayed);
+ }
+ message += Messages.BackToMap;
+ message += Messages.MetaTerminator;
+ return message;
+ }
public static string BuildPrivateNotes(User user)
{
string message = "";
diff --git a/Horse Isle Server/Horse Isle Server/Player/Highscore.cs b/Horse Isle Server/Horse Isle Server/Player/Highscore.cs
index 50caf9b..eab2998 100644
--- a/Horse Isle Server/Horse Isle Server/Player/Highscore.cs
+++ b/Horse Isle Server/Horse Isle Server/Player/Highscore.cs
@@ -1,31 +1,106 @@
using HISP.Server;
-
+using System.Collections.Generic;
namespace HISP.Player
{
class Highscore
{
- public static bool RegisterHighscore(int playerId, string gameTitle, int score, bool time)
+ public class HighscoreTableEntry
+ {
+ public int UserId;
+ public string GameName;
+ public int Wins;
+ public int Looses;
+ public int TimesPlayed;
+ public int Score;
+ public string Type;
+ }
+ public HighscoreTableEntry[] HighscoreList
+ {
+ get
+ {
+ return highScoreList.ToArray();
+ }
+ }
+
+ private User baseUser;
+ private List highScoreList = new List();
+
+ public Highscore(User user)
+ {
+ baseUser = user;
+ HighscoreTableEntry[] highscores = Database.GetPlayerHighScores(user.Id);
+ foreach (HighscoreTableEntry highscore in highscores)
+ highScoreList.Add(highscore);
+ }
+ public HighscoreTableEntry GetHighscore(string gameTitle)
+ {
+ foreach (HighscoreTableEntry highscore in HighscoreList)
+ {
+ if (highscore.GameName == gameTitle)
+ {
+ return highscore;
+ }
+ }
+ throw new KeyNotFoundException("Highscore for " + gameTitle + " Not found.");
+ }
+ public bool HasHighscore(string gameTitle)
+ {
+ foreach(HighscoreTableEntry highscore in HighscoreList)
+ {
+ if(highscore.GameName == gameTitle)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool UpdateHighscore(string gameTitle, int score, bool time)
{
bool isNewScore = true;
- if (!Database.PlayerHasHighscore(playerId, gameTitle))
+ if (!HasHighscore(gameTitle))
{
- Database.AddNewHighscore(playerId, gameTitle, score, time ? "TIME" : "SCORE");
+ string type = time ? "TIME" : "SCORE";
+ Database.AddNewHighscore(baseUser.Id, gameTitle, score, type);
+
+ HighscoreTableEntry newHighscore = new HighscoreTableEntry();
+ newHighscore.UserId = baseUser.Id;
+ newHighscore.GameName = gameTitle;
+ newHighscore.Wins = 0;
+ newHighscore.Looses = 0;
+ newHighscore.TimesPlayed = 1;
+ newHighscore.Score = score;
+ newHighscore.Type = type;
+ highScoreList.Add(newHighscore);
+
return isNewScore;
}
else
{
- int currentScore = Database.GetHighscore(playerId, gameTitle);
+ int currentScore = GetHighscore(gameTitle).Score;
if (score < currentScore)
{
score = currentScore;
isNewScore = false;
}
- Database.UpdateHighscore(playerId, gameTitle, score);
+ Database.UpdateHighscore(baseUser.Id, gameTitle, score);
+
+ for(int i = 0; i < highScoreList.Count; i++)
+ {
+
+ if(highScoreList[i].GameName == gameTitle)
+ {
+ highScoreList[i].TimesPlayed += 1;
+ highScoreList[i].Score = score;
+ }
+ }
+
return isNewScore;
}
+
}
}
}
diff --git a/Horse Isle Server/Horse Isle Server/Player/User.cs b/Horse Isle Server/Horse Isle Server/Player/User.cs
index d15b4d3..0c0fce8 100644
--- a/Horse Isle Server/Horse Isle Server/Player/User.cs
+++ b/Horse Isle Server/Horse Isle Server/Player/User.cs
@@ -39,6 +39,7 @@ namespace HISP.Player
public Npc.NpcEntry LastTalkedToNpc;
public Shop LastShoppedAt;
public PlayerQuests Quests;
+ public Highscore Highscores;
public int FreeMinutes
{
get
@@ -372,7 +373,7 @@ namespace HISP.Player
Gender = Database.GetGender(UserId);
MailBox = new Mailbox(this);
-
+ Highscores = new Highscore(this);
// Generate SecCodes
diff --git a/Horse Isle Server/Horse Isle Server/Server/Database.cs b/Horse Isle Server/Horse Isle Server/Server/Database.cs
index 296014d..228f45d 100644
--- a/Horse Isle Server/Horse Isle Server/Server/Database.cs
+++ b/Horse Isle Server/Horse Isle Server/Server/Database.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using MySqlConnector;
using HISP.Game;
+using HISP.Player;
namespace HISP.Server
{
@@ -1688,22 +1689,6 @@ namespace HISP.Server
}
}
- public static bool PlayerHasHighscore(int playerId, string gameTitle)
- {
- using (MySqlConnection db = new MySqlConnection(ConnectionString))
- {
- db.Open();
- MySqlCommand sqlCommand = db.CreateCommand();
- sqlCommand.CommandText = "SELECT COUNT(1) FROM Leaderboards WHERE playerId=@playerId AND minigame=@gameTitle";
- sqlCommand.Parameters.AddWithValue("@playerId", playerId);
- sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
- sqlCommand.Prepare();
- int count = Convert.ToInt32(sqlCommand.ExecuteScalar());
-
- sqlCommand.Dispose();
- return count >= 1;
- }
- }
public static void AddNewHighscore(int playerId, string gameTitle, int score, string type)
{
using (MySqlConnection db = new MySqlConnection(ConnectionString))
@@ -1724,20 +1709,90 @@ namespace HISP.Server
}
}
- public static int GetHighscore(int playerId, string gameTitle)
+ public static Highscore.HighscoreTableEntry[] GetPlayerHighScores(int playerId)
{
+ List entires = new List();
using (MySqlConnection db = new MySqlConnection(ConnectionString))
{
db.Open();
MySqlCommand sqlCommand = db.CreateCommand();
- sqlCommand.CommandText = "SELECT score FROM Leaderboards WHERE playerId=@playerId AND minigame=@gameTitle";
+ sqlCommand.CommandText = "SELECT * FROM leaderboards WHERE playerId=@playerId ORDER BY score ASC";
sqlCommand.Parameters.AddWithValue("@playerId", playerId);
- sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
sqlCommand.Prepare();
- int score = Convert.ToInt32(sqlCommand.ExecuteScalar());
+ MySqlDataReader reader = sqlCommand.ExecuteReader();
+
+ while (reader.Read())
+ {
+ Highscore.HighscoreTableEntry highscoreEntry = new Highscore.HighscoreTableEntry();
+ highscoreEntry.UserId = reader.GetInt32(0);
+ highscoreEntry.GameName = reader.GetString(1);
+ highscoreEntry.Wins = reader.GetInt32(2);
+ highscoreEntry.Looses = reader.GetInt32(3);
+ highscoreEntry.TimesPlayed = reader.GetInt32(4);
+ highscoreEntry.Score = reader.GetInt32(5);
+ highscoreEntry.Type = reader.GetString(6);
+ entires.Add(highscoreEntry);
+ }
+
sqlCommand.Dispose();
- return score;
+ return entires.ToArray();
+ }
+ }
+
+ public static Highscore.HighscoreTableEntry[] GetTopScores(string gameTitle, int limit)
+ {
+ List entires = new List();
+ using (MySqlConnection db = new MySqlConnection(ConnectionString))
+ {
+ db.Open();
+ MySqlCommand sqlCommand = db.CreateCommand();
+ sqlCommand.CommandText = "SELECT * FROM leaderboards WHERE minigame=@gameTitle ORDER BY score ASC LIMIT @limit";
+ sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
+ sqlCommand.Parameters.AddWithValue("@limit", limit);
+ sqlCommand.Prepare();
+ MySqlDataReader reader = sqlCommand.ExecuteReader();
+
+ while(reader.Read())
+ {
+ Highscore.HighscoreTableEntry highscoreEntry = new Highscore.HighscoreTableEntry();
+ highscoreEntry.UserId = reader.GetInt32(0);
+ highscoreEntry.GameName = gameTitle;
+ highscoreEntry.Wins = reader.GetInt32(2);
+ highscoreEntry.Looses = reader.GetInt32(3);
+ highscoreEntry.TimesPlayed = reader.GetInt32(4);
+ highscoreEntry.Score = reader.GetInt32(5);
+ highscoreEntry.Type = reader.GetString(6);
+ entires.Add(highscoreEntry);
+ }
+
+
+ sqlCommand.Dispose();
+ return entires.ToArray();
+ }
+ }
+
+ public static int GetRanking(int score, string gameTitle)
+ {
+ using (MySqlConnection db = new MySqlConnection(ConnectionString))
+ {
+
+ db.Open();
+ MySqlCommand sqlCommand = db.CreateCommand();
+ sqlCommand.CommandText = "SELECT DISTINCT score FROM leaderboards WHERE minigame=@gameTitle ORDER BY score ASC";
+ sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
+ sqlCommand.Prepare();
+ MySqlDataReader reader = sqlCommand.ExecuteReader();
+ int i = 1;
+ while(reader.Read())
+ {
+ if (reader.GetInt32(0) == score)
+ break;
+ i++;
+ }
+
+ sqlCommand.Dispose();
+ return i;
}
}
public static void UpdateHighscore(int playerId, string gameTitle, int score)
diff --git a/Horse Isle Server/Horse Isle Server/Server/GameDataJson.cs b/Horse Isle Server/Horse Isle Server/Server/GameDataJson.cs
index c63b615..1bcd70e 100644
--- a/Horse Isle Server/Horse Isle Server/Server/GameDataJson.cs
+++ b/Horse Isle Server/Horse Isle Server/Server/GameDataJson.cs
@@ -550,6 +550,17 @@ namespace HISP.Server
Messages.NoPitchforkMeta = gameData.messages.meta.hay_pile.no_pitchfork;
Messages.HasPitchforkMeta = gameData.messages.meta.hay_pile.pitchfork;
+ // Highscore
+
+ Messages.HighscoreHeaderMeta = gameData.messages.meta.highscores.header_meta;
+ Messages.HighscoreFormat = gameData.messages.meta.highscores.highscore_format;
+ Messages.BestTimeFormat = gameData.messages.meta.highscores.besttime_format;
+
+ Messages.GameHighScoreHeaderFormat = gameData.messages.meta.highscores.game_highscore_header;
+ Messages.GameHighScoreFormat = gameData.messages.meta.highscores.game_highscore_format;
+
+ Messages.GameBestTimeHeaderFormat = gameData.messages.meta.highscores.game_besttime_header;
+ Messages.GameBestTimeFormat = gameData.messages.meta.highscores.game_besttime_format;
// Sec Codes
diff --git a/Horse Isle Server/Horse Isle Server/Server/GameServer.cs b/Horse Isle Server/Horse Isle Server/Server/GameServer.cs
index 4df020d..e896c5c 100644
--- a/Horse Isle Server/Horse Isle Server/Server/GameServer.cs
+++ b/Horse Isle Server/Horse Isle Server/Server/GameServer.cs
@@ -158,6 +158,10 @@ namespace HISP.Server
metaPacket = PacketBuilder.CreateMetaPacket(Meta.BuildPrivateNotes(sender.LoggedinUser));
sender.SendPacket(metaPacket);
break;
+ case 20:
+ metaPacket = PacketBuilder.CreateMetaPacket(Meta.BuildMinigameRankingsForUser(sender.LoggedinUser));
+ sender.SendPacket(metaPacket);
+ break;
default:
Logger.ErrorPrint("Dynamic button #" + buttonId + " unknown...");
break;
@@ -349,7 +353,7 @@ namespace HISP.Server
return;
}
- bool newHighscore = Highscore.RegisterHighscore(sender.LoggedinUser.Id, gameTitle, value, time);
+ bool newHighscore = sender.LoggedinUser.Highscores.UpdateHighscore(gameTitle, value, time);
if (newHighscore && !time)
{
byte[] chatPacket = PacketBuilder.CreateChat(Messages.FormatHighscoreBeatenMessage(value), PacketBuilder.CHAT_BOTTOM_RIGHT);
@@ -530,7 +534,21 @@ namespace HISP.Server
return;
}
}
-
+ else if(method == PacketBuilder.PLAYERINFO_HIGHSCORES_LIST)
+ {
+ string packetStr = Encoding.UTF8.GetString(packet);
+ string gameName = packetStr.Substring(2, packetStr.Length - 4);
+ byte[] metaTag = PacketBuilder.CreateMetaPacket(Meta.BuildTopHighscores(gameName));
+ sender.SendPacket(metaTag);
+ }
+ else if (method == PacketBuilder.PLAYERINFO_BESTTIMES_LIST)
+ {
+ string packetStr = Encoding.UTF8.GetString(packet);
+ string gameName = packetStr.Substring(2, packetStr.Length - 4);
+ byte[] metaTag = PacketBuilder.CreateMetaPacket(Meta.BuildTopTimes(gameName));
+ sender.SendPacket(metaTag);
+ }
+
}
public static void OnMovementPacket(GameClient sender, byte[] packet)
@@ -1728,7 +1746,7 @@ namespace HISP.Server
byte[] loginMessageBytes = PacketBuilder.CreateChat(Messages.FormatLoginMessage(sender.LoggedinUser.Username), PacketBuilder.CHAT_BOTTOM_LEFT);
foreach (GameClient client in ConnectedClients)
if (client.LoggedIn)
- if (!client.LoggedinUser.MuteLogins || client.LoggedinUser.MuteAll)
+ if (!client.LoggedinUser.MuteLogins && !client.LoggedinUser.MuteAll)
if (client.LoggedinUser.Id != userId)
client.SendPacket(loginMessageBytes);
@@ -1747,7 +1765,6 @@ namespace HISP.Server
public static void OnDisconnect(GameClient sender)
{
connectedClients.Remove(sender);
- Logger.DebugPrint("owoo disconnect");
if (sender.LoggedIn)
{
Database.RemoveOnlineUser(sender.LoggedinUser.Id);
@@ -1755,7 +1772,7 @@ namespace HISP.Server
byte[] logoutMessageBytes = PacketBuilder.CreateChat(Messages.FormatLogoutMessage(sender.LoggedinUser.Username), PacketBuilder.CHAT_BOTTOM_LEFT);
foreach (GameClient client in ConnectedClients)
if (client.LoggedIn)
- if (!client.LoggedinUser.MuteLogins)
+ if (!client.LoggedinUser.MuteLogins && !client.LoggedinUser.MuteAll)
if (client.LoggedinUser.Id != sender.LoggedinUser.Id)
client.SendPacket(logoutMessageBytes);
// Tell clients of diconnect (remove from chat)
diff --git a/Horse Isle Server/Horse Isle Server/Server/PacketBuilder.cs b/Horse Isle Server/Horse Isle Server/Server/PacketBuilder.cs
index 089a1fa..33d561a 100644
--- a/Horse Isle Server/Horse Isle Server/Server/PacketBuilder.cs
+++ b/Horse Isle Server/Horse Isle Server/Server/PacketBuilder.cs
@@ -40,18 +40,22 @@ namespace HISP.Server
public const byte PACKET_PLAYERINFO = 0x16;
public const byte PACKET_INFORMATION = 0x28;
+
public const byte SECCODE_QUEST = 0x32;
public const byte SECCODE_ITEM = 0x28;
public const byte SECCODE_SCORE = 0x3D;
public const byte SECCODE_TIME = 0x3E;
- public const byte SECCODE_MONEY = 0x1E;
+ public const byte SECCODE_MONEY = 0x1E;
+
public const byte NPC_START_CHAT = 0x14;
public const byte NPC_CONTINUE_CHAT = 0x15;
public const byte PLAYERINFO_LEAVE = 0x16;
public const byte PLAYERINFO_UPDATE_OR_CREATE = 0x15;
-
+ public const byte PLAYERINFO_HIGHSCORES_LIST = 0x51;
+ public const byte PLAYERINFO_BESTTIMES_LIST = 0x52;
+
public const byte VIEW_PROFILE = 0x14;
public const byte SAVE_PROFILE = 0x15;