Removed NioTSO client and server

- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
This commit is contained in:
Tony Bark 2024-05-01 02:55:43 -04:00
parent f12ba1502b
commit 22191ce648
591 changed files with 53264 additions and 3362 deletions

View file

@ -0,0 +1,103 @@
using FSO.Common.Utils;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace FSO.Common.Rendering.Emoji
{
public class EmojiCache
{
public string Source = "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/";
public int DefaultRes = 24;
public int Width = 32;
public int NextIndex = 0;
public List<string> Emojis = new List<string>();
public Dictionary<string, int> EmojiToIndex = new Dictionary<string, int>();
public RenderTarget2D EmojiTex;
public SpriteBatch EmojiBatch;
public HashSet<int> IncompleteSpaces = new HashSet<int>();
public HashSet<int> ErrorSpaces = new HashSet<int>();
private GraphicsDevice GD;
private bool needClear = true;
public EmojiCache(GraphicsDevice gd)
{
GD = gd;
EmojiBatch = new SpriteBatch(gd);
EmojiTex = new RenderTarget2D(gd, Width * DefaultRes, Width * DefaultRes, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents);
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
}
public void ExpandIfNeeded()
{
//todo
}
public Rectangle GetEmoji(string emojiID) {
int index;
if (EmojiToIndex.TryGetValue(emojiID, out index))
{
return RectForIndex(index);
} else
{
index = NextIndex++;
ExpandIfNeeded();
lock (IncompleteSpaces) IncompleteSpaces.Add(index);
var client = new WebClient();
client.DownloadDataCompleted += (object sender, DownloadDataCompletedEventArgs e) =>
{
if (e.Cancelled || e.Error != null || e.Result == null)
{
lock (ErrorSpaces) ErrorSpaces.Add(index);
} else
{
GameThread.NextUpdate(x =>
{
try
{
using (var mem = new MemoryStream(e.Result))
{
var tex = Texture2D.FromStream(GD, mem);
var decimated = TextureUtils.Decimate(tex, GD, 72 / DefaultRes, true);
//blit this into our emoji buffer
GD.SetRenderTarget(EmojiTex);
if (needClear)
{
GD.Clear(Color.TransparentBlack);
needClear = false;
}
EmojiBatch.Begin(blendState: BlendState.NonPremultiplied, sortMode: SpriteSortMode.Immediate);
EmojiBatch.Draw(decimated, RectForIndex(index), Color.White);
EmojiBatch.End();
GD.SetRenderTarget(null);
}
}
catch (Exception)
{
lock (ErrorSpaces) ErrorSpaces.Add(index);
}
});
}
lock (IncompleteSpaces) IncompleteSpaces.Remove(index);
};
client.DownloadDataAsync(new Uri((emojiID[0] == '!')?(emojiID.Substring(1)):(Source + emojiID + ".png")));
Emojis.Add(emojiID);
EmojiToIndex[emojiID] = index;
return RectForIndex(index);
}
}
private Rectangle RectForIndex(int index)
{
return new Rectangle((index % Width) * DefaultRes, (index / Width) * DefaultRes, DefaultRes, DefaultRes);
}
}
}

View file

@ -0,0 +1,95 @@
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace FSO.Common.Rendering.Emoji
{
public class EmojiDictionary
{
public Dictionary<string, string> NameToEmojis = new Dictionary<string, string>();
public Dictionary<string, List<string>> KeywordToCandidates = new Dictionary<string, List<string>>();
public Dictionary<string, List<string>> CandidatesToKeywords = new Dictionary<string, List<string>>();
public EmojiDictionary()
{
JObject emojis;
using (var emojiDict = new StreamReader(
new FileStream(Path.Combine(FSOEnvironment.ContentDir, "UI/emojis.json"), FileMode.Open, FileAccess.Read, FileShare.Read)))
{
emojis = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(emojiDict.ReadToEnd());
}
foreach (var token in emojis)
{
var charValue = token.Value.Value<string>("char");
var twemojiID = ToCodePoint(charValue);
NameToEmojis[token.Key] = twemojiID;
var keys = token.Value.Value<JArray>("keywords");
foreach (var key in keys)
AddKeyword(key.Value<string>(), token.Key);
}
using (var emojiDict = new StreamReader(
new FileStream(Path.Combine(FSOEnvironment.ContentDir, "UI/customemojis.json"), FileMode.Open, FileAccess.Read, FileShare.Read)))
{
emojis = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(emojiDict.ReadToEnd());
}
foreach (var token in emojis)
{
NameToEmojis[token.Key] = "!" + token.Value.Value<string>("url");
var keys = token.Value.Value<JArray>("keywords");
foreach (var key in keys)
AddKeyword(key.Value<string>(), token.Key);
}
}
public void AddKeyword(string keyword, string candidate)
{
List<string> cand;
if (!KeywordToCandidates.TryGetValue(keyword, out cand))
{
cand = new List<string>();
KeywordToCandidates[keyword] = cand;
}
cand.Add(candidate);
if (!CandidatesToKeywords.TryGetValue(candidate, out cand))
{
cand = new List<string>();
CandidatesToKeywords[candidate] = cand;
}
cand.Add(keyword);
}
public string ToCodePoint(string str)
{
var cs = str.ToCharArray();
var i = 0;
var c = 0;
var p = 0;
var r = new List<string>();
var zeroWidth = str.Any(x => x == '\x200D');
while (i < cs.Length)
{
c = cs[i++];
if (c == 0xfe0f && !zeroWidth) continue; //"as image", just ignore this
if (p > 0)
{
r.Add((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).ToString("x"));
p = 0;
}
else if (0xD800 <= c && c <= 0xDBFF)
{
p = c;
}
else
{
r.Add(c.ToString("x"));
}
}
return string.Join("-", r);
}
}
}

View file

@ -0,0 +1,232 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
namespace FSO.Common.Rendering.Emoji
{
public class EmojiProvider
{
public EmojiDictionary Dict;
public EmojiCache Cache;
public Random DictRand = new Random();
public Dictionary<string, string> TranslateShortcuts = new Dictionary<string, string>()
{
{ "i", "eye" },
{ "you", "point_right" },
{ "your", "point_right" },
{ "you're", "point_right" },
{ "me", "relieved: :point_left" },
{ "i'm", "relieved: :point_left" },
{ "us", "couple" },
{ "we", "couple" },
{ "our", "couple" },
{ "what", "woman_shrugging" },
{ "be", "b" },
{ "to", "two" },
{ "and", "symbols" },
{ "that", "point_up" },
{ "for", "four" },
{ "not", "exclamation" },
{ "this", "point_up" },
{ "but", "exclamation" },
{ "his", "man" },
{ "her", "woman" },
{ "him", "man" },
{ "he", "man" },
{ "she", "woman" },
{ "from", "postbox" },
{ "they", "family_man_woman_girl_boy" },
{ "them", "family_man_woman_girl_boy" },
{ "or", "thinking" },
{ "an", "a" },
{ "will", "thinking" },
{ "my", "relieved: :point_left" },
{ "all", "rainbow_flag" },
{ "would", "tree" },
{ "so", "woman_shrugging" },
{ "out", "outbox_tray" },
{ "if", "thinking" },
{ "about", "arrows_counterclockwise" },
{ "who", "thinking: :family_man_woman_girl_boy" },
{ "get", "gift" },
{ "which", "woman_shrugging" },
{ "go", "door" },
{ "when", "watch" },
{ "make", "toolbox" },
{ "know", "brain" },
{ "take", "takeout_box" },
{ "into", "arrow_heading_down" },
{ "year", "calendar" },
{ "because", "woman_shrugging" },
{ "hmm", "thinking" },
{ "yo", "wave" },
{ "hey", "wave" },
{ "sup", "wave" },
};
public EmojiProvider(GraphicsDevice gd)
{
Dict = new EmojiDictionary();
Cache = new EmojiCache(gd);
}
public string EmojiFromName(string name)
{
string result;
if (Dict.NameToEmojis.TryGetValue(name, out result))
return result;
return null;
}
private string StripPunctuation(string word, out string punctuation)
{
for (int i=word.Length-1; i>=0; i--)
{
if (!char.IsPunctuation(word[i]))
{
punctuation = word.Substring(i + 1);
return word.Substring(0, i + 1);
}
}
punctuation = "";
return word;
}
public string SearchForWordHit(string word)
{
string direct;
if (TranslateShortcuts.TryGetValue(word, out direct))
{
return ":" + direct + ":";
}
if (Dict.NameToEmojis.TryGetValue(word, out direct))
{
return ":" + word + ":";
}
List<string> options;
if (Dict.KeywordToCandidates.TryGetValue(word, out options))
{
return ":" + options[DictRand.Next(options.Count)] + ":";
}
return null;
}
public string TranslateWordToEmoji(string word)
{
string punctuation;
var lower = StripPunctuation(word.ToLowerInvariant(), out punctuation);
string result;
result = SearchForWordHit(lower);
if (result != null) return result + ' ' + punctuation;
if (lower.EndsWith("s"))
{
result = SearchForWordHit(lower.Substring(0, lower.Length-1));
if (result != null) return result + ' ' + punctuation;
}
if (lower.EndsWith("ing"))
{
result = SearchForWordHit(lower.Substring(0, lower.Length - 3));
if (result != null) return result + ' ' + punctuation;
}
return word.Substring(0, lower.Length) + punctuation;
}
public Tuple<Texture2D, Rectangle> GetEmoji(string id)
{
var rect = Cache.GetEmoji(id);
return new Tuple<Texture2D, Rectangle>(Cache.EmojiTex, rect);
}
public string EmojiToBB(string input)
{
//search through the string for emojis to turn to BBcode
int index = 0;
int lastColon = -1;
var result = new StringBuilder();
while (true)
{
var nColon = input.IndexOf(':', index);
if (nColon == -1) break;
if (lastColon == -1) result.Append(input.Substring(index, nColon - index)); //add up to the colon
else
{
//is the string between the two colons an emoji?
var emoji = EmojiFromName(input.Substring(lastColon + 1, nColon - (lastColon + 1)));
if (emoji == null)
{
result.Append(":"+input.Substring(index, nColon - index)); //add up to the colon (include the last colon we skipped)
} else
{
result.Append("[emoji=" + emoji + "] ");
lastColon = -1;
index = nColon + 1;
continue;
}
}
index = nColon + 1;
lastColon = nColon;
}
result.Append(((lastColon == -1) ? "" : ":") + input.Substring(index));
return result.ToString();
}
public string EmojiTranslate(string input)
{
//search for replacement candidates for each word in input
var words = input.Split(' ');
for (int i=0; i<words.Length; i++)
{
var word = words[i];
if (word == "") continue;
if (word.Length > 2 && word.StartsWith(":") && word.EndsWith(":"))
{
//is this already an emoji? if so, skip it
var existing = EmojiFromName(word.Substring(1, word.Length-2));
if (existing == null) continue;
}
words[i] = TranslateWordToEmoji(word);
}
return String.Join(" ", words);
}
public string EmojiOnly(string input, int mode)
{
if (mode == 2) return EmojiTranslate(input);
//search through the string for emojis to keep
int index = 0;
int lastColon = -1;
var result = new StringBuilder();
while (true)
{
var nColon = input.IndexOf(':', index);
if (nColon == -1) break;
else
{
//is the string between the two colons an emoji?
var name = input.Substring(lastColon + 1, nColon - (lastColon + 1));
var emoji = EmojiFromName(name);
if (emoji == null)
{
}
else
{
result.Append(":" + name + ": ");
lastColon = -1;
index = nColon + 1;
continue;
}
}
index = nColon + 1;
lastColon = nColon;
}
//result.Append(((lastColon == -1) ? "" : ":"));
return result.ToString();
}
}
}