Revert "Edgegap Plugin: switch to Package Manager dependency (#3845)"

This reverts commit 890f4e6c0b.
This commit is contained in:
mischa 2024-07-25 21:12:36 +02:00
parent 890f4e6c0b
commit d4ee07694b
292 changed files with 15468 additions and 28 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b13bce90dfb604c2d9170e3640f59ad9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b7c51dc3e45095f4a8a960150837fe7b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
# Edgegap Servers Plugin Changelog
Please refer to our [Github repository](https://github.com/edgegap/edgegap-unity-plugin/releases) for up-to-date changelog.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e3f5a876d3822ce4e884e3c8d026fbcc
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14024ce6d2e64d5ba58ab20409ac648f
timeCreated: 1701785018

View File

@ -0,0 +1,704 @@
// MIRROR CHANGE: drop in Codice.Utils HttpUtility subset to not depend on Unity's plastic scm package
// SOURCE: Unity Plastic SCM package
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace Edgegap.Codice.Utils // MIRROR CHANGE: namespace Edgegap.* to not collide if anyone has Plastic SCM installed already
{
public class HttpEncoder
{
private static char[] hexChars = "0123456789abcdef".ToCharArray();
private static object entitiesLock = new object();
private static SortedDictionary<string, char> entities;
private static HttpEncoder defaultEncoder = new HttpEncoder();
private static HttpEncoder currentEncoder = HttpEncoder.defaultEncoder;
private static IDictionary<string, char> Entities
{
get
{
lock (HttpEncoder.entitiesLock)
{
if (HttpEncoder.entities == null)
HttpEncoder.InitEntities();
return (IDictionary<string, char>) HttpEncoder.entities;
}
}
}
public static HttpEncoder Current
{
get => HttpEncoder.currentEncoder;
set => HttpEncoder.currentEncoder = value != null ? value : throw new ArgumentNullException(nameof (value));
}
public static HttpEncoder Default => HttpEncoder.defaultEncoder;
protected internal virtual void HeaderNameValueEncode(
string headerName,
string headerValue,
out string encodedHeaderName,
out string encodedHeaderValue)
{
encodedHeaderName = !string.IsNullOrEmpty(headerName) ? HttpEncoder.EncodeHeaderString(headerName) : headerName;
if (string.IsNullOrEmpty(headerValue))
encodedHeaderValue = headerValue;
else
encodedHeaderValue = HttpEncoder.EncodeHeaderString(headerValue);
}
private static void StringBuilderAppend(string s, ref StringBuilder sb)
{
if (sb == null)
sb = new StringBuilder(s);
else
sb.Append(s);
}
private static string EncodeHeaderString(string input)
{
StringBuilder sb = (StringBuilder) null;
for (int index = 0; index < input.Length; ++index)
{
char ch = input[index];
if (ch < ' ' && ch != '\t' || ch == '\u007F')
HttpEncoder.StringBuilderAppend(string.Format("%{0:x2}", (object) (int) ch), ref sb);
}
return sb != null ? sb.ToString() : input;
}
protected internal virtual void HtmlAttributeEncode(string value, TextWriter output)
{
if (output == null)
throw new ArgumentNullException(nameof (output));
if (string.IsNullOrEmpty(value))
return;
output.Write(HttpEncoder.HtmlAttributeEncode(value));
}
protected internal virtual void HtmlDecode(string value, TextWriter output)
{
if (output == null)
throw new ArgumentNullException(nameof (output));
output.Write(HttpEncoder.HtmlDecode(value));
}
protected internal virtual void HtmlEncode(string value, TextWriter output)
{
if (output == null)
throw new ArgumentNullException(nameof (output));
output.Write(HttpEncoder.HtmlEncode(value));
}
protected internal virtual byte[] UrlEncode(byte[] bytes, int offset, int count) => HttpEncoder.UrlEncodeToBytes(bytes, offset, count);
protected internal virtual string UrlPathEncode(string value)
{
if (string.IsNullOrEmpty(value))
return value;
MemoryStream result = new MemoryStream();
int length = value.Length;
for (int index = 0; index < length; ++index)
HttpEncoder.UrlPathEncodeChar(value[index], (Stream) result);
return Encoding.ASCII.GetString(result.ToArray());
}
internal static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
{
int num1 = bytes != null ? bytes.Length : throw new ArgumentNullException(nameof (bytes));
if (num1 == 0)
return new byte[0];
if (offset < 0 || offset >= num1)
throw new ArgumentOutOfRangeException(nameof (offset));
if (count < 0 || count > num1 - offset)
throw new ArgumentOutOfRangeException(nameof (count));
MemoryStream result = new MemoryStream(count);
int num2 = offset + count;
for (int index = offset; index < num2; ++index)
HttpEncoder.UrlEncodeChar((char) bytes[index], (Stream) result, false);
return result.ToArray();
}
internal static string HtmlEncode(string s)
{
switch (s)
{
case "":
return string.Empty;
case null:
return (string) null;
default:
bool flag = false;
for (int index = 0; index < s.Length; ++index)
{
char ch = s[index];
if (ch == '&' || ch == '"' || ch == '<' || ch == '>' || ch > '\u009F' || ch == '\'')
{
flag = true;
break;
}
}
if (!flag)
return s;
StringBuilder stringBuilder = new StringBuilder();
int length = s.Length;
for (int index = 0; index < length; ++index)
{
char ch = s[index];
switch (ch)
{
case '"':
stringBuilder.Append("&quot;");
break;
case '&':
stringBuilder.Append("&amp;");
break;
case '\'':
stringBuilder.Append("&#39;");
break;
case '<':
stringBuilder.Append("&lt;");
break;
case '>':
stringBuilder.Append("&gt;");
break;
case '':
stringBuilder.Append("&#65308;");
break;
case '':
stringBuilder.Append("&#65310;");
break;
default:
if (ch > '\u009F' && ch < 'Ā')
{
stringBuilder.Append("&#");
stringBuilder.Append(((int) ch).ToString((IFormatProvider) CultureInfo.InvariantCulture));
stringBuilder.Append(";");
break;
}
stringBuilder.Append(ch);
break;
}
}
return stringBuilder.ToString();
}
}
internal static string HtmlAttributeEncode(string s)
{
if (string.IsNullOrEmpty(s))
return string.Empty;
bool flag = false;
for (int index = 0; index < s.Length; ++index)
{
char ch = s[index];
int num;
switch (ch)
{
case '"':
case '&':
case '<':
num = 0;
break;
default:
num = ch != '\'' ? 1 : 0;
break;
}
if (num == 0)
{
flag = true;
break;
}
}
if (!flag)
return s;
StringBuilder stringBuilder = new StringBuilder();
int length = s.Length;
for (int index = 0; index < length; ++index)
{
char ch = s[index];
switch (ch)
{
case '"':
stringBuilder.Append("&quot;");
break;
case '&':
stringBuilder.Append("&amp;");
break;
case '\'':
stringBuilder.Append("&#39;");
break;
case '<':
stringBuilder.Append("&lt;");
break;
default:
stringBuilder.Append(ch);
break;
}
}
return stringBuilder.ToString();
}
internal static string HtmlDecode(string s)
{
switch (s)
{
case "":
return string.Empty;
case null:
return (string) null;
default:
if (s.IndexOf('&') == -1)
return s;
StringBuilder stringBuilder1 = new StringBuilder();
StringBuilder stringBuilder2 = new StringBuilder();
StringBuilder stringBuilder3 = new StringBuilder();
int length = s.Length;
int num1 = 0;
int num2 = 0;
bool flag1 = false;
bool flag2 = false;
for (int index = 0; index < length; ++index)
{
char ch = s[index];
if (num1 == 0)
{
if (ch == '&')
{
stringBuilder2.Append(ch);
stringBuilder1.Append(ch);
num1 = 1;
}
else
stringBuilder3.Append(ch);
}
else if (ch == '&')
{
num1 = 1;
if (flag2)
{
stringBuilder2.Append(num2.ToString((IFormatProvider) CultureInfo.InvariantCulture));
flag2 = false;
}
stringBuilder3.Append(stringBuilder2.ToString());
stringBuilder2.Length = 0;
stringBuilder2.Append('&');
}
else
{
switch (num1)
{
case 1:
if (ch == ';')
{
num1 = 0;
stringBuilder3.Append(stringBuilder2.ToString());
stringBuilder3.Append(ch);
stringBuilder2.Length = 0;
break;
}
num2 = 0;
flag1 = false;
num1 = ch == '#' ? 3 : 2;
stringBuilder2.Append(ch);
stringBuilder1.Append(ch);
break;
case 2:
stringBuilder2.Append(ch);
if (ch == ';')
{
string str = stringBuilder2.ToString();
if (str.Length > 1 && HttpEncoder.Entities.ContainsKey(str.Substring(1, str.Length - 2)))
str = HttpEncoder.Entities[str.Substring(1, str.Length - 2)].ToString();
stringBuilder3.Append(str);
num1 = 0;
stringBuilder2.Length = 0;
stringBuilder1.Length = 0;
break;
}
break;
case 3:
if (ch == ';')
{
if (num2 == 0)
stringBuilder3.Append(stringBuilder1.ToString() + ";");
else if (num2 > (int) ushort.MaxValue)
{
stringBuilder3.Append("&#");
stringBuilder3.Append(num2.ToString((IFormatProvider) CultureInfo.InvariantCulture));
stringBuilder3.Append(";");
}
else
stringBuilder3.Append((char) num2);
num1 = 0;
stringBuilder2.Length = 0;
stringBuilder1.Length = 0;
flag2 = false;
}
else if (flag1 && Uri.IsHexDigit(ch))
{
num2 = num2 * 16 + Uri.FromHex(ch);
flag2 = true;
stringBuilder1.Append(ch);
}
else if (char.IsDigit(ch))
{
num2 = num2 * 10 + ((int) ch - 48);
flag2 = true;
stringBuilder1.Append(ch);
}
else if (num2 == 0 && (ch == 'x' || ch == 'X'))
{
flag1 = true;
stringBuilder1.Append(ch);
}
else
{
num1 = 2;
if (flag2)
{
stringBuilder2.Append(num2.ToString((IFormatProvider) CultureInfo.InvariantCulture));
flag2 = false;
}
stringBuilder2.Append(ch);
}
break;
}
}
}
if (stringBuilder2.Length > 0)
stringBuilder3.Append(stringBuilder2.ToString());
else if (flag2)
stringBuilder3.Append(num2.ToString((IFormatProvider) CultureInfo.InvariantCulture));
return stringBuilder3.ToString();
}
}
internal static bool NotEncoded(char c) => c == '!' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_';
internal static void UrlEncodeChar(char c, Stream result, bool isUnicode)
{
if (c > 'ÿ')
{
int num = (int) c;
result.WriteByte((byte) 37);
result.WriteByte((byte) 117);
int index1 = num >> 12;
result.WriteByte((byte) HttpEncoder.hexChars[index1]);
int index2 = num >> 8 & 15;
result.WriteByte((byte) HttpEncoder.hexChars[index2]);
int index3 = num >> 4 & 15;
result.WriteByte((byte) HttpEncoder.hexChars[index3]);
int index4 = num & 15;
result.WriteByte((byte) HttpEncoder.hexChars[index4]);
}
else if (c > ' ' && HttpEncoder.NotEncoded(c))
result.WriteByte((byte) c);
else if (c == ' ')
result.WriteByte((byte) 43);
else if (c < '0' || c < 'A' && c > '9' || c > 'Z' && c < 'a' || c > 'z')
{
if (isUnicode && c > '\u007F')
{
result.WriteByte((byte) 37);
result.WriteByte((byte) 117);
result.WriteByte((byte) 48);
result.WriteByte((byte) 48);
}
else
result.WriteByte((byte) 37);
int index5 = (int) c >> 4;
result.WriteByte((byte) HttpEncoder.hexChars[index5]);
int index6 = (int) c & 15;
result.WriteByte((byte) HttpEncoder.hexChars[index6]);
}
else
result.WriteByte((byte) c);
}
internal static void UrlPathEncodeChar(char c, Stream result)
{
if (c < '!' || c > '~')
{
byte[] bytes = Encoding.UTF8.GetBytes(c.ToString());
for (int index1 = 0; index1 < bytes.Length; ++index1)
{
result.WriteByte((byte) 37);
int index2 = (int) bytes[index1] >> 4;
result.WriteByte((byte) HttpEncoder.hexChars[index2]);
int index3 = (int) bytes[index1] & 15;
result.WriteByte((byte) HttpEncoder.hexChars[index3]);
}
}
else if (c == ' ')
{
result.WriteByte((byte) 37);
result.WriteByte((byte) 50);
result.WriteByte((byte) 48);
}
else
result.WriteByte((byte) c);
}
private static void InitEntities()
{
HttpEncoder.entities = new SortedDictionary<string, char>((IComparer<string>) StringComparer.Ordinal);
HttpEncoder.entities.Add("nbsp", ' ');
HttpEncoder.entities.Add("iexcl", '¡');
HttpEncoder.entities.Add("cent", '¢');
HttpEncoder.entities.Add("pound", '£');
HttpEncoder.entities.Add("curren", '¤');
HttpEncoder.entities.Add("yen", '¥');
HttpEncoder.entities.Add("brvbar", '¦');
HttpEncoder.entities.Add("sect", '§');
HttpEncoder.entities.Add("uml", '¨');
HttpEncoder.entities.Add("copy", '©');
HttpEncoder.entities.Add("ordf", 'ª');
HttpEncoder.entities.Add("laquo", '«');
HttpEncoder.entities.Add("not", '¬');
HttpEncoder.entities.Add("shy", '\u00AD');
HttpEncoder.entities.Add("reg", '®');
HttpEncoder.entities.Add("macr", '¯');
HttpEncoder.entities.Add("deg", '°');
HttpEncoder.entities.Add("plusmn", '±');
HttpEncoder.entities.Add("sup2", '\u00B2');
HttpEncoder.entities.Add("sup3", '\u00B3');
HttpEncoder.entities.Add("acute", '´');
HttpEncoder.entities.Add("micro", 'µ');
HttpEncoder.entities.Add("para", '¶');
HttpEncoder.entities.Add("middot", '·');
HttpEncoder.entities.Add("cedil", '¸');
HttpEncoder.entities.Add("sup1", '\u00B9');
HttpEncoder.entities.Add("ordm", 'º');
HttpEncoder.entities.Add("raquo", '»');
HttpEncoder.entities.Add("frac14", '\u00BC');
HttpEncoder.entities.Add("frac12", '\u00BD');
HttpEncoder.entities.Add("frac34", '\u00BE');
HttpEncoder.entities.Add("iquest", '¿');
HttpEncoder.entities.Add("Agrave", 'À');
HttpEncoder.entities.Add("Aacute", 'Á');
HttpEncoder.entities.Add("Acirc", 'Â');
HttpEncoder.entities.Add("Atilde", 'Ã');
HttpEncoder.entities.Add("Auml", 'Ä');
HttpEncoder.entities.Add("Aring", 'Å');
HttpEncoder.entities.Add("AElig", 'Æ');
HttpEncoder.entities.Add("Ccedil", 'Ç');
HttpEncoder.entities.Add("Egrave", 'È');
HttpEncoder.entities.Add("Eacute", 'É');
HttpEncoder.entities.Add("Ecirc", 'Ê');
HttpEncoder.entities.Add("Euml", 'Ë');
HttpEncoder.entities.Add("Igrave", 'Ì');
HttpEncoder.entities.Add("Iacute", 'Í');
HttpEncoder.entities.Add("Icirc", 'Î');
HttpEncoder.entities.Add("Iuml", 'Ï');
HttpEncoder.entities.Add("ETH", 'Ð');
HttpEncoder.entities.Add("Ntilde", 'Ñ');
HttpEncoder.entities.Add("Ograve", 'Ò');
HttpEncoder.entities.Add("Oacute", 'Ó');
HttpEncoder.entities.Add("Ocirc", 'Ô');
HttpEncoder.entities.Add("Otilde", 'Õ');
HttpEncoder.entities.Add("Ouml", 'Ö');
HttpEncoder.entities.Add("times", '×');
HttpEncoder.entities.Add("Oslash", 'Ø');
HttpEncoder.entities.Add("Ugrave", 'Ù');
HttpEncoder.entities.Add("Uacute", 'Ú');
HttpEncoder.entities.Add("Ucirc", 'Û');
HttpEncoder.entities.Add("Uuml", 'Ü');
HttpEncoder.entities.Add("Yacute", 'Ý');
HttpEncoder.entities.Add("THORN", 'Þ');
HttpEncoder.entities.Add("szlig", 'ß');
HttpEncoder.entities.Add("agrave", 'à');
HttpEncoder.entities.Add("aacute", 'á');
HttpEncoder.entities.Add("acirc", 'â');
HttpEncoder.entities.Add("atilde", 'ã');
HttpEncoder.entities.Add("auml", 'ä');
HttpEncoder.entities.Add("aring", 'å');
HttpEncoder.entities.Add("aelig", 'æ');
HttpEncoder.entities.Add("ccedil", 'ç');
HttpEncoder.entities.Add("egrave", 'è');
HttpEncoder.entities.Add("eacute", 'é');
HttpEncoder.entities.Add("ecirc", 'ê');
HttpEncoder.entities.Add("euml", 'ë');
HttpEncoder.entities.Add("igrave", 'ì');
HttpEncoder.entities.Add("iacute", 'í');
HttpEncoder.entities.Add("icirc", 'î');
HttpEncoder.entities.Add("iuml", 'ï');
HttpEncoder.entities.Add("eth", 'ð');
HttpEncoder.entities.Add("ntilde", 'ñ');
HttpEncoder.entities.Add("ograve", 'ò');
HttpEncoder.entities.Add("oacute", 'ó');
HttpEncoder.entities.Add("ocirc", 'ô');
HttpEncoder.entities.Add("otilde", 'õ');
HttpEncoder.entities.Add("ouml", 'ö');
HttpEncoder.entities.Add("divide", '÷');
HttpEncoder.entities.Add("oslash", 'ø');
HttpEncoder.entities.Add("ugrave", 'ù');
HttpEncoder.entities.Add("uacute", 'ú');
HttpEncoder.entities.Add("ucirc", 'û');
HttpEncoder.entities.Add("uuml", 'ü');
HttpEncoder.entities.Add("yacute", 'ý');
HttpEncoder.entities.Add("thorn", 'þ');
HttpEncoder.entities.Add("yuml", 'ÿ');
HttpEncoder.entities.Add("fnof", 'ƒ');
HttpEncoder.entities.Add("Alpha", 'Α');
HttpEncoder.entities.Add("Beta", 'Β');
HttpEncoder.entities.Add("Gamma", 'Γ');
HttpEncoder.entities.Add("Delta", 'Δ');
HttpEncoder.entities.Add("Epsilon", 'Ε');
HttpEncoder.entities.Add("Zeta", 'Ζ');
HttpEncoder.entities.Add("Eta", 'Η');
HttpEncoder.entities.Add("Theta", 'Θ');
HttpEncoder.entities.Add("Iota", 'Ι');
HttpEncoder.entities.Add("Kappa", 'Κ');
HttpEncoder.entities.Add("Lambda", 'Λ');
HttpEncoder.entities.Add("Mu", 'Μ');
HttpEncoder.entities.Add("Nu", 'Ν');
HttpEncoder.entities.Add("Xi", 'Ξ');
HttpEncoder.entities.Add("Omicron", 'Ο');
HttpEncoder.entities.Add("Pi", 'Π');
HttpEncoder.entities.Add("Rho", 'Ρ');
HttpEncoder.entities.Add("Sigma", 'Σ');
HttpEncoder.entities.Add("Tau", 'Τ');
HttpEncoder.entities.Add("Upsilon", 'Υ');
HttpEncoder.entities.Add("Phi", 'Φ');
HttpEncoder.entities.Add("Chi", 'Χ');
HttpEncoder.entities.Add("Psi", 'Ψ');
HttpEncoder.entities.Add("Omega", 'Ω');
HttpEncoder.entities.Add("alpha", 'α');
HttpEncoder.entities.Add("beta", 'β');
HttpEncoder.entities.Add("gamma", 'γ');
HttpEncoder.entities.Add("delta", 'δ');
HttpEncoder.entities.Add("epsilon", 'ε');
HttpEncoder.entities.Add("zeta", 'ζ');
HttpEncoder.entities.Add("eta", 'η');
HttpEncoder.entities.Add("theta", 'θ');
HttpEncoder.entities.Add("iota", 'ι');
HttpEncoder.entities.Add("kappa", 'κ');
HttpEncoder.entities.Add("lambda", 'λ');
HttpEncoder.entities.Add("mu", 'μ');
HttpEncoder.entities.Add("nu", 'ν');
HttpEncoder.entities.Add("xi", 'ξ');
HttpEncoder.entities.Add("omicron", 'ο');
HttpEncoder.entities.Add("pi", 'π');
HttpEncoder.entities.Add("rho", 'ρ');
HttpEncoder.entities.Add("sigmaf", 'ς');
HttpEncoder.entities.Add("sigma", 'σ');
HttpEncoder.entities.Add("tau", 'τ');
HttpEncoder.entities.Add("upsilon", 'υ');
HttpEncoder.entities.Add("phi", 'φ');
HttpEncoder.entities.Add("chi", 'χ');
HttpEncoder.entities.Add("psi", 'ψ');
HttpEncoder.entities.Add("omega", 'ω');
HttpEncoder.entities.Add("thetasym", 'ϑ');
HttpEncoder.entities.Add("upsih", 'ϒ');
HttpEncoder.entities.Add("piv", 'ϖ');
HttpEncoder.entities.Add("bull", '•');
HttpEncoder.entities.Add("hellip", '…');
HttpEncoder.entities.Add("prime", '');
HttpEncoder.entities.Add("Prime", '″');
HttpEncoder.entities.Add("oline", '‾');
HttpEncoder.entities.Add("frasl", '');
HttpEncoder.entities.Add("weierp", '℘');
HttpEncoder.entities.Add("image", '');
HttpEncoder.entities.Add("real", '');
HttpEncoder.entities.Add("trade", '™');
HttpEncoder.entities.Add("alefsym", 'ℵ');
HttpEncoder.entities.Add("larr", '←');
HttpEncoder.entities.Add("uarr", '↑');
HttpEncoder.entities.Add("rarr", '→');
HttpEncoder.entities.Add("darr", '↓');
HttpEncoder.entities.Add("harr", '↔');
HttpEncoder.entities.Add("crarr", '↵');
HttpEncoder.entities.Add("lArr", '⇐');
HttpEncoder.entities.Add("uArr", '⇑');
HttpEncoder.entities.Add("rArr", '⇒');
HttpEncoder.entities.Add("dArr", '⇓');
HttpEncoder.entities.Add("hArr", '⇔');
HttpEncoder.entities.Add("forall", '∀');
HttpEncoder.entities.Add("part", '∂');
HttpEncoder.entities.Add("exist", '∃');
HttpEncoder.entities.Add("empty", '∅');
HttpEncoder.entities.Add("nabla", '∇');
HttpEncoder.entities.Add("isin", '∈');
HttpEncoder.entities.Add("notin", '∉');
HttpEncoder.entities.Add("ni", '∋');
HttpEncoder.entities.Add("prod", '∏');
HttpEncoder.entities.Add("sum", '∑');
HttpEncoder.entities.Add("minus", '');
HttpEncoder.entities.Add("lowast", '');
HttpEncoder.entities.Add("radic", '√');
HttpEncoder.entities.Add("prop", '∝');
HttpEncoder.entities.Add("infin", '∞');
HttpEncoder.entities.Add("ang", '∠');
HttpEncoder.entities.Add("and", '∧');
HttpEncoder.entities.Add("or", '');
HttpEncoder.entities.Add("cap", '∩');
HttpEncoder.entities.Add("cup", '');
HttpEncoder.entities.Add("int", '∫');
HttpEncoder.entities.Add("there4", '∴');
HttpEncoder.entities.Add("sim", '');
HttpEncoder.entities.Add("cong", '≅');
HttpEncoder.entities.Add("asymp", '≈');
HttpEncoder.entities.Add("ne", '≠');
HttpEncoder.entities.Add("equiv", '≡');
HttpEncoder.entities.Add("le", '≤');
HttpEncoder.entities.Add("ge", '≥');
HttpEncoder.entities.Add("sub", '⊂');
HttpEncoder.entities.Add("sup", '⊃');
HttpEncoder.entities.Add("nsub", '⊄');
HttpEncoder.entities.Add("sube", '⊆');
HttpEncoder.entities.Add("supe", '⊇');
HttpEncoder.entities.Add("oplus", '⊕');
HttpEncoder.entities.Add("otimes", '⊗');
HttpEncoder.entities.Add("perp", '⊥');
HttpEncoder.entities.Add("sdot", '⋅');
HttpEncoder.entities.Add("lceil", '⌈');
HttpEncoder.entities.Add("rceil", '⌉');
HttpEncoder.entities.Add("lfloor", '⌊');
HttpEncoder.entities.Add("rfloor", '⌋');
HttpEncoder.entities.Add("lang", '〈');
HttpEncoder.entities.Add("rang", '〉');
HttpEncoder.entities.Add("loz", '◊');
HttpEncoder.entities.Add("spades", '♠');
HttpEncoder.entities.Add("clubs", '♣');
HttpEncoder.entities.Add("hearts", '♥');
HttpEncoder.entities.Add("diams", '♦');
HttpEncoder.entities.Add("quot", '"');
HttpEncoder.entities.Add("amp", '&');
HttpEncoder.entities.Add("lt", '<');
HttpEncoder.entities.Add("gt", '>');
HttpEncoder.entities.Add("OElig", 'Œ');
HttpEncoder.entities.Add("oelig", 'œ');
HttpEncoder.entities.Add("Scaron", 'Š');
HttpEncoder.entities.Add("scaron", 'š');
HttpEncoder.entities.Add("Yuml", 'Ÿ');
HttpEncoder.entities.Add("circ", 'ˆ');
HttpEncoder.entities.Add("tilde", '˜');
HttpEncoder.entities.Add("ensp", '');
HttpEncoder.entities.Add("emsp", '');
HttpEncoder.entities.Add("thinsp", '');
HttpEncoder.entities.Add("zwnj", '\u200C');
HttpEncoder.entities.Add("zwj", '\u200D');
HttpEncoder.entities.Add("lrm", '\u200E');
HttpEncoder.entities.Add("rlm", '\u200F');
HttpEncoder.entities.Add("ndash", '');
HttpEncoder.entities.Add("mdash", '—');
HttpEncoder.entities.Add("lsquo", '');
HttpEncoder.entities.Add("rsquo", '');
HttpEncoder.entities.Add("sbquo", '');
HttpEncoder.entities.Add("ldquo", '“');
HttpEncoder.entities.Add("rdquo", '”');
HttpEncoder.entities.Add("bdquo", '„');
HttpEncoder.entities.Add("dagger", '†');
HttpEncoder.entities.Add("Dagger", '‡');
HttpEncoder.entities.Add("permil", '‰');
HttpEncoder.entities.Add("lsaquo", '');
HttpEncoder.entities.Add("rsaquo", '');
HttpEncoder.entities.Add("euro", '€');
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c9f699c227f48e381db521abe59c2e1
timeCreated: 1701789490

View File

@ -0,0 +1,230 @@
// MIRROR CHANGE: drop in Codice.Utils HttpUtility subset to not depend on Unity's plastic scm package
// SOURCE: Unity Plastic SCM package
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
namespace Edgegap.Codice.Utils // MIRROR CHANGE: namespace Edgegap.* to not collide if anyone has Plastic SCM installed already
{
public sealed class HttpUtility
{
private static void WriteCharBytes(IList buf, char ch, Encoding e)
{
if (ch > 'ÿ')
{
Encoding encoding = e;
char[] chars = new char[1]{ ch };
foreach (byte num in encoding.GetBytes(chars))
buf.Add((object) num);
}
else
buf.Add((object) (byte) ch);
}
public static string UrlDecode(string s, Encoding e)
{
if (null == s)
return (string) null;
if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
return s;
if (e == null)
e = Encoding.UTF8;
long length = (long) s.Length;
List<byte> buf = new List<byte>();
for (int index = 0; (long) index < length; ++index)
{
char ch = s[index];
if (ch == '%' && (long) (index + 2) < length && s[index + 1] != '%')
{
if (s[index + 1] == 'u' && (long) (index + 5) < length)
{
int num = HttpUtility.GetChar(s, index + 2, 4);
if (num != -1)
{
HttpUtility.WriteCharBytes((IList) buf, (char) num, e);
index += 5;
}
else
HttpUtility.WriteCharBytes((IList) buf, '%', e);
}
else
{
int num;
if ((num = HttpUtility.GetChar(s, index + 1, 2)) != -1)
{
HttpUtility.WriteCharBytes((IList) buf, (char) num, e);
index += 2;
}
else
HttpUtility.WriteCharBytes((IList) buf, '%', e);
}
}
else if (ch == '+')
HttpUtility.WriteCharBytes((IList) buf, ' ', e);
else
HttpUtility.WriteCharBytes((IList) buf, ch, e);
}
byte[] array = buf.ToArray();
return e.GetString(array);
}
private static int GetInt(byte b)
{
char ch = (char) b;
if (ch >= '0' && ch <= '9')
return (int) ch - 48;
if (ch >= 'a' && ch <= 'f')
return (int) ch - 97 + 10;
return ch >= 'A' && ch <= 'F' ? (int) ch - 65 + 10 : -1;
}
private static int GetChar(string str, int offset, int length)
{
int num1 = 0;
int num2 = length + offset;
for (int index = offset; index < num2; ++index)
{
char b = str[index];
if (b > '\u007F')
return -1;
int num3 = HttpUtility.GetInt((byte) b);
if (num3 == -1)
return -1;
num1 = (num1 << 4) + num3;
}
return num1;
}
public static string UrlEncode(string str) => HttpUtility.UrlEncode(str, Encoding.UTF8);
public static string UrlEncode(string s, Encoding Enc)
{
if (s == null)
return (string) null;
if (s == string.Empty)
return string.Empty;
bool flag = false;
int length = s.Length;
for (int index = 0; index < length; ++index)
{
char c = s[index];
if ((c < '0' || c < 'A' && c > '9' || c > 'Z' && c < 'a' || c > 'z') && !HttpEncoder.NotEncoded(c))
{
flag = true;
break;
}
}
if (!flag)
return s;
byte[] bytes1 = new byte[Enc.GetMaxByteCount(s.Length)];
int bytes2 = Enc.GetBytes(s, 0, s.Length, bytes1, 0);
return Encoding.ASCII.GetString(HttpUtility.UrlEncodeToBytes(bytes1, 0, bytes2));
}
public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) => bytes == null ? (byte[]) null : HttpEncoder.Current.UrlEncode(bytes, offset, count);
public static string HtmlDecode(string s)
{
if (s == null)
return (string) null;
using (StringWriter output = new StringWriter())
{
HttpEncoder.Current.HtmlDecode(s, (TextWriter) output);
return output.ToString();
}
}
public static NameValueCollection ParseQueryString(string query) => HttpUtility.ParseQueryString(query, Encoding.UTF8);
public static NameValueCollection ParseQueryString(
string query,
Encoding encoding)
{
if (query == null)
throw new ArgumentNullException(nameof (query));
if (encoding == null)
throw new ArgumentNullException(nameof (encoding));
if (query.Length == 0 || query.Length == 1 && query[0] == '?')
return (NameValueCollection) new HttpUtility.HttpQSCollection();
if (query[0] == '?')
query = query.Substring(1);
NameValueCollection result = (NameValueCollection) new HttpUtility.HttpQSCollection();
HttpUtility.ParseQueryString(query, encoding, result);
return result;
}
internal static void ParseQueryString(
string query,
Encoding encoding,
NameValueCollection result)
{
if (query.Length == 0)
return;
string str1 = HttpUtility.HtmlDecode(query);
int length = str1.Length;
int num1 = 0;
bool flag = true;
while (num1 <= length)
{
int startIndex = -1;
int num2 = -1;
for (int index = num1; index < length; ++index)
{
if (startIndex == -1 && str1[index] == '=')
startIndex = index + 1;
else if (str1[index] == '&')
{
num2 = index;
break;
}
}
if (flag)
{
flag = false;
if (str1[num1] == '?')
++num1;
}
string name;
if (startIndex == -1)
{
name = (string) null;
startIndex = num1;
}
else
name = HttpUtility.UrlDecode(str1.Substring(num1, startIndex - num1 - 1), encoding);
if (num2 < 0)
{
num1 = -1;
num2 = str1.Length;
}
else
num1 = num2 + 1;
string str2 = HttpUtility.UrlDecode(str1.Substring(startIndex, num2 - startIndex), encoding);
result.Add(name, str2);
if (num1 == -1)
break;
}
}
private sealed class HttpQSCollection : NameValueCollection
{
public override string ToString()
{
int count = this.Count;
if (count == 0)
return "";
StringBuilder stringBuilder = new StringBuilder();
string[] allKeys = this.AllKeys;
for (int index = 0; index < count; ++index)
stringBuilder.AppendFormat("{0}={1}&", (object) allKeys[index], (object) HttpUtility.UrlEncode(this[allKeys[index]]));
if (stringBuilder.Length > 0)
--stringBuilder.Length;
return stringBuilder.ToString();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6f83f468a8b546fd92606db56038f9e6
timeCreated: 1701785025

View File

@ -0,0 +1,16 @@
FROM ubuntu:bionic
ARG DEBIAN_FRONTEND=noninteractive
COPY Builds/EdgegapServer /root/build/
WORKDIR /root/
RUN chmod +x /root/build/ServerBuild
RUN apt-get update && \
apt-get install -y ca-certificates && \
apt-get clean && \
update-ca-certificates
ENTRYPOINT [ "/root/build/ServerBuild", "-batchmode", "-nographics"]

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 78b80371aabba1d48aac39ec7ccfe7c5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
{
"name": "Edgegap",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.nuget.newtonsoft-json",
"expression": "",
"define": "NEWTONSOFT_JSON"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 58ff3a2ca929d114eaf0ca373ff1e07a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 635b395f47dc9f742b4d71144921bb0d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6d2a4589d6738cb4b82bb1ceebd1453f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,280 @@
using System;
using System.Collections.Specialized;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
// using Codice.Utils; // MIRROR CHANGE
using Edgegap.Codice.Utils; // MIRROR CHANGE
using UnityEngine;
namespace Edgegap.Editor.Api
{
/// <summary>
/// Handles base URL and common methods for all Edgegap APIs.
/// </summary>
public abstract class EdgegapApiBase
{
#region Vars
private readonly HttpClient _httpClient = new HttpClient(); // Base address set // MIRROR CHANGE: Unity 2020 support
protected ApiEnvironment SelectedApiEnvironment { get; }
protected EdgegapWindowMetadata.LogLevel LogLevel { get; set; }
protected bool IsLogLevelDebug => LogLevel == EdgegapWindowMetadata.LogLevel.Debug;
/// <summary>Based on SelectedApiEnvironment.</summary>
/// <returns></returns>
private string GetBaseUrl() =>
SelectedApiEnvironment == ApiEnvironment.Staging
? ApiEnvironment.Staging.GetApiUrl()
: ApiEnvironment.Console.GetApiUrl();
#endregion // Vars
/// <param name="apiEnvironment">"console" || "staging-console"?</param>
/// <param name="apiToken">Without the "token " prefix, although we'll clear this if present</param>
/// <param name="logLevel">You may want more-verbose logs other than errs</param>
protected EdgegapApiBase(
ApiEnvironment apiEnvironment,
string apiToken,
EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error)
{
this.SelectedApiEnvironment = apiEnvironment;
this._httpClient.BaseAddress = new Uri($"{GetBaseUrl()}/");
this._httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string cleanedApiToken = apiToken.Replace("token ", ""); // We already prefixed token below
this._httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("token", cleanedApiToken);
this.LogLevel = logLevel;
}
#region HTTP Requests
/// <summary>
/// POST | We already added "https://api.edgegap.com/" (or similar) BaseAddress via constructor.
/// </summary>
/// <param name="relativePath"></param>
/// <param name="json">Serialize to your model via Newtonsoft</param>
/// <returns>
/// - Success => returns HttpResponseMessage result
/// - Error => Catches errs => returns null (no rethrow)
/// </returns>
protected async Task<HttpResponseMessage> PostAsync(string relativePath = "", string json = "{}")
{
StringContent stringContent = CreateStringContent(json);
Uri uri = new Uri(_httpClient.BaseAddress, relativePath); // Normalize POST uri: Can't end with `/`.
if (IsLogLevelDebug)
Debug.Log($"PostAsync to: `{uri}` with json: `{json}`");
try
{
return await ExecuteRequestAsync(() => _httpClient.PostAsync(uri, stringContent));
}
catch (Exception e)
{
Debug.LogError($"Error: {e}");
throw;
}
}
/// <summary>
/// PATCH | We already added "https://api.edgegap.com/" (or similar) BaseAddress via constructor.
/// </summary>
/// <param name="relativePath"></param>
/// <param name="json">Serialize to your model via Newtonsoft</param>
/// <returns>
/// - Success => returns HttpResponseMessage result
/// - Error => Catches errs => returns null (no rethrow)
/// </returns>
protected async Task<HttpResponseMessage> PatchAsync(string relativePath = "", string json = "{}")
{
StringContent stringContent = CreateStringContent(json);
Uri uri = new Uri(_httpClient.BaseAddress, relativePath); // Normalize PATCH uri: Can't end with `/`.
if (IsLogLevelDebug)
Debug.Log($"PatchAsync to: `{uri}` with json: `{json}`");
// (!) As of 11/15/2023, .PatchAsync() is "unsupported by Unity" -- so we manually set the verb and SendAsync()
// Create the request manually
HttpRequestMessage patchRequest = new HttpRequestMessage(new HttpMethod("PATCH"), uri)
{
Content = stringContent,
};
try
{
return await ExecuteRequestAsync(() => _httpClient.SendAsync(patchRequest));
}
catch (Exception e)
{
Debug.LogError($"Error: {e}");
throw;
}
}
/// <summary>
/// GET | We already added "https://api.edgegap.com/" (or similar) BaseAddress via constructor.
/// </summary>
/// <param name="relativePath"></param>
/// <param name="customQuery">
/// To append to the URL; eg: "foo=0&bar=1"
/// (!) First query key should prefix nothing, as shown</param>
/// <returns>
/// - Success => returns HttpResponseMessage result
/// - Error => Catches errs => returns null (no rethrow)
/// </returns>
protected async Task<HttpResponseMessage> GetAsync(string relativePath = "", string customQuery = "")
{
string completeRelativeUri = prepareEdgegapUriWithQuery(
relativePath,
customQuery);
if (IsLogLevelDebug)
Debug.Log($"GetAsync to: `{completeRelativeUri} with customQuery: `{customQuery}`");
try
{
return await ExecuteRequestAsync(() => _httpClient.GetAsync(completeRelativeUri));
}
catch (Exception e)
{
Debug.LogError($"Error: {e}");
throw;
}
}
/// <summary>
/// DELETE | We already added "https://api.edgegap.com/" (or similar) BaseAddress via constructor.
/// </summary>
/// <param name="relativePath"></param>
/// <param name="customQuery">
/// To append to the URL; eg: "foo=0&bar=1"
/// (!) First query key should prefix nothing, as shown</param>
/// <returns>
/// - Success => returns HttpResponseMessage result
/// - Error => Catches errs => returns null (no rethrow)
/// </returns>
protected async Task<HttpResponseMessage> DeleteAsync(string relativePath = "", string customQuery = "")
{
string completeRelativeUri = prepareEdgegapUriWithQuery(
relativePath,
customQuery);
if (IsLogLevelDebug)
Debug.Log($"DeleteAsync to: `{completeRelativeUri} with customQuery: `{customQuery}`");
try
{
return await ExecuteRequestAsync(() => _httpClient.DeleteAsync(completeRelativeUri));
}
catch (Exception e)
{
Debug.LogError($"Error: {e}");
throw;
}
}
/// <summary>POST || GET</summary>
/// <param name="requestFunc"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<HttpResponseMessage> ExecuteRequestAsync(
Func<Task<HttpResponseMessage>> requestFunc,
CancellationToken cancellationToken = default)
{
HttpResponseMessage response = null;
try
{
response = await requestFunc();
}
catch (HttpRequestException e)
{
Debug.LogError($"HttpRequestException: {e.Message}");
return null;
}
catch (TaskCanceledException e)
{
if (cancellationToken.IsCancellationRequested)
Debug.LogError("Task was cancelled by caller.");
else
Debug.LogError($"TaskCanceledException: Timeout - {e.Message}");
return null;
}
catch (Exception e) // Generic exception handler
{
Debug.LogError($"Unexpected error occurred: {e.Message}");
return null;
}
// Check for a successful status code
if (response == null)
{
Debug.Log("!Success (null response) - returning 500");
return CreateUnknown500Err();
}
if (!response.IsSuccessStatusCode)
{
HttpMethod httpMethod = response.RequestMessage.Method;
Debug.Log($"!Success: {(short)response.StatusCode} {response.ReasonPhrase} - " +
$"{httpMethod} | {response.RequestMessage.RequestUri}` - " +
$"{response.Content?.ReadAsStringAsync().Result}");
}
return response;
}
#endregion // HTTP Requests
#region Utils
/// <summary>Creates a UTF-8 encoded application/json + json obj</summary>
/// <param name="json">Arbitrary json obj</param>
/// <returns></returns>
private StringContent CreateStringContent(string json = "{}") =>
new StringContent(json, Encoding.UTF8, "application/json"); // MIRROR CHANGE: 'new()' not supported in Unity 2020
private static HttpResponseMessage CreateUnknown500Err() =>
new HttpResponseMessage(HttpStatusCode.InternalServerError); // 500 - Unknown // MIRROR CHANGE: 'new()' not supported in Unity 2020
/// <summary>
/// Merges Edgegap-required query params (source) -> merges with custom query -> normalizes.
/// </summary>
/// <param name="relativePath"></param>
/// <param name="customQuery"></param>
/// <returns></returns>
private string prepareEdgegapUriWithQuery(string relativePath, string customQuery)
{
// Create UriBuilder using the BaseAddress
UriBuilder uriBuilder = new UriBuilder(_httpClient.BaseAddress);
// Add the relative path to the UriBuilder's path
uriBuilder.Path += relativePath;
// Parse the existing query from the UriBuilder
NameValueCollection query = HttpUtility.ParseQueryString(uriBuilder.Query);
// Add default "source=unity" param
query["source"] = "unity";
// Parse and merge the custom query parameters
NameValueCollection customParams = HttpUtility.ParseQueryString(customQuery);
foreach (string key in customParams)
{
query[key] = customParams[key];
}
// Set the merged query back to the UriBuilder
uriBuilder.Query = query.ToString();
// Extract the complete relative URI and return it
return uriBuilder.Uri.PathAndQuery;
}
#endregion // Utils
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50379f30f5137224aa05a5c7b6b5ebba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,148 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Edgegap.Editor.Api.Models.Requests;
using Edgegap.Editor.Api.Models.Results;
namespace Edgegap.Editor.Api
{
/// <summary>
/// Wraps the v1/app API endpoint: Applications Control API.
/// - API Doc | https://docs.edgegap.com/api/#tag/Applications
/// </summary>
public class EdgegapAppApi : EdgegapApiBase
{
public EdgegapAppApi(
ApiEnvironment apiEnvironment,
string apiToken,
EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error)
: base(apiEnvironment, apiToken, logLevel)
{
}
#region API Methods
/// <summary>
/// POST to v1/app
/// - Create an application that will regroup application versions.
/// - API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/application-post
/// </summary>
/// <returns>
/// Http info with GetCreateAppResult data model
/// - Success: 200
/// - Fail: 409 (app already exists), 400 (reached limit)
/// </returns>
public async Task<EdgegapHttpResult<GetCreateAppResult>> CreateApp(CreateAppRequest request)
{
HttpResponseMessage response = await PostAsync("v1/app", request.ToString());
EdgegapHttpResult<GetCreateAppResult> result = new EdgegapHttpResult<GetCreateAppResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
/// <summary>
/// GET to v1/app
/// - Get an application that will regroup application versions.
/// - API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/application-post
/// </summary>
/// <returns>
/// Http info with GetCreateAppResult data model
/// - Success: 200
/// </returns>
public async Task<EdgegapHttpResult<GetCreateAppResult>> GetApp(string appName)
{
HttpResponseMessage response = await GetAsync($"v1/app/{appName}");
EdgegapHttpResult<GetCreateAppResult> result = new EdgegapHttpResult<GetCreateAppResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
/// <summary>
/// PATCH to v1/app/{app_name}/version/{version_name}
/// - Update an *existing* application version with new specifications.
/// - API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-versions-patch
/// </summary>
/// <returns>
/// Http info with UpdateAppVersionRequest data model
/// - Success: 200
/// </returns>
public async Task<EdgegapHttpResult<UpsertAppVersionResult>> UpdateAppVersion(UpdateAppVersionRequest request)
{
string relativePath = $"v1/app/{request.AppName}/version/{request.VersionName}";
HttpResponseMessage response = await PatchAsync(relativePath, request.ToString());
EdgegapHttpResult<UpsertAppVersionResult> result = new EdgegapHttpResult<UpsertAppVersionResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
/// <summary>
/// POST to v1/app/{app_name}/version
/// - Create an new application version with new specifications.
/// - API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-version-post
/// </summary>
/// <returns>
/// Http info with UpdateAppVersionRequest data model
/// - Success: 200 (no result model)
/// - Fail: 409 (app already exists), 400 (reached limit)
/// </returns>
public async Task<EdgegapHttpResult<UpsertAppVersionResult>> CreateAppVersion(CreateAppVersionRequest request)
{
string relativePath = $"v1/app/{request.AppName}/version";
HttpResponseMessage response = await PostAsync(relativePath, request.ToString());
EdgegapHttpResult<UpsertAppVersionResult> result = new EdgegapHttpResult<UpsertAppVersionResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
#endregion // API Methods
#region Chained API Methods
/// <summary>
/// PATCH and/or POST to v1/app/: Upsert an *existing* application version with new specifications.
/// - Consumes either 1 or 2 API calls: 1st tries to PATCH, then POST if PATCH fails (!exists).
/// - API POST Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-version-post
/// - API PATCH Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-versions-patch
/// </summary>
/// <returns>
/// Http info with UpdateAppVersionRequest data model
/// - Success: 200 (no result model)
/// - Fail: 409 (app already exists), 400 (reached limit)
/// </returns>
public async Task<EdgegapHttpResult<UpsertAppVersionResult>> UpsertAppVersion(UpdateAppVersionRequest request)
{
EdgegapHttpResult<UpsertAppVersionResult> result = await UpdateAppVersion(request); // PATCH
if (result.HasErr)
{
// Try to create, instead
CreateAppVersionRequest createAppVersionRequest = CreateAppVersionRequest.FromUpdateRequest(request);
result = await CreateAppVersion(createAppVersionRequest); // POST
}
bool isSuccess = result.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
#endregion // Chained API Methods
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b0b3b865abe64b49a4000294c4e9593
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,177 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Edgegap.Editor.Api.Models.Requests;
using Edgegap.Editor.Api.Models.Results;
using UnityEngine.Assertions;
namespace Edgegap.Editor.Api
{
/// <summary>
/// Wraps the v1/[deploy | status | stop] API endpoints: Deployments Control API.
/// - API Doc | https://docs.edgegap.com/api/#tag/Deployments
/// </summary>
public class EdgegapDeploymentsApi : EdgegapApiBase
{
public EdgegapDeploymentsApi(
ApiEnvironment apiEnvironment,
string apiToken,
EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error)
: base(apiEnvironment, apiToken, logLevel)
{
}
#region API Methods
/// <summary>
/// POST v1/deploy
/// - Create a new deployment. Deployment is a server instance of your application version.
/// - API Doc | https://docs.edgegap.com/api/#tag/Deployments
/// </summary>
/// <returns>
/// Http info with CreateDeploymentResult data model
/// - Success: 200
/// </returns>
public async Task<EdgegapHttpResult<CreateDeploymentResult>> CreateDeploymentAsync(
CreateDeploymentRequest request)
{
HttpResponseMessage response = await PostAsync("v1/deploy", request.ToString());
EdgegapHttpResult<CreateDeploymentResult> result = new EdgegapHttpResult<CreateDeploymentResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
/// <summary>
/// GET v1/status/{requestId}
/// - Retrieve the information for a deployment.
/// - API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deployment-status-get
/// </summary>
/// <param name="requestId">
/// Unique Identifier to keep track of your request across all Arbitrium ecosystem.
/// It's included in the response of the app deploy. Ex: "93924761ccde"</param>
/// <returns>
/// Http info with GetDeploymentStatusResult data model
/// - Success: 200
/// </returns>
public async Task<EdgegapHttpResult<GetDeploymentStatusResult>> GetDeploymentStatusAsync(string requestId)
{
HttpResponseMessage response = await GetAsync($"v1/status/{requestId}");
EdgegapHttpResult<GetDeploymentStatusResult> result = new EdgegapHttpResult<GetDeploymentStatusResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
/// <summary>
/// DELETE v1/stop/{requestId}
/// - Delete an instance of deployment. It will stop the running container and all its games.
/// - API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deployment-status-get
/// </summary>
/// <param name="requestId">
/// Unique Identifier to keep track of your request across all Arbitrium ecosystem.
/// It's included in the response of the app deploy. Ex: "93924761ccde"</param>
/// <returns>
/// Http info with GetDeploymentStatusResult data model
/// - Success: 200
/// </returns>
public async Task<EdgegapHttpResult<StopActiveDeploymentResult>> StopActiveDeploymentAsync(string requestId)
{
HttpResponseMessage response = await DeleteAsync($"v1/stop/{requestId}");
EdgegapHttpResult<StopActiveDeploymentResult> result = new EdgegapHttpResult<StopActiveDeploymentResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
#endregion // API Methods
#region Chained API Methods
/// <summary>
/// POST v1/deploy => GET v1/status/{requestId}
/// - Create a new deployment. Deployment is a server instance of your application version.
/// - Then => await READY status.
/// - API Doc | https://docs.edgegap.com/api/#tag/Deployments
/// </summary>
/// <returns>
/// Http info with CreateDeploymentResult data model (with a READY deployment status)
/// - Success: 200
/// - Error: If createResult.HasErr, returns createResult
/// </returns>
public async Task<EdgegapHttpResult<CreateDeploymentResult>> CreateDeploymentAwaitReadyStatusAsync(
CreateDeploymentRequest request, TimeSpan pollInterval)
{
EdgegapHttpResult<CreateDeploymentResult> createResponse = await CreateDeploymentAsync(request);
// Create =>
bool isCreateSuccess = createResponse.StatusCode == HttpStatusCode.OK; // 200
if (!isCreateSuccess)
return createResponse;
// Await Status READY =>
string requestId = createResponse.Data.RequestId;
_ = await AwaitReadyStatusAsync(requestId, pollInterval);
// Return no matter what the result; no need to validate
return createResponse;
}
/// <summary>If you recently deployed but want to await READY status.</summary>
/// <param name="requestId"></param>
/// <param name="pollInterval"></param>
public async Task<EdgegapHttpResult<GetDeploymentStatusResult>> AwaitReadyStatusAsync(
string requestId,
TimeSpan pollInterval)
{
Assert.IsTrue(!string.IsNullOrEmpty(requestId)); // Validate
EdgegapHttpResult<GetDeploymentStatusResult> statusResponse = null;
CancellationTokenSource cts = new CancellationTokenSource (TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020
EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS));
bool isReady = false;
while (!isReady && !cts.Token.IsCancellationRequested)
{
await Task.Delay(pollInterval, cts.Token);
statusResponse = await GetDeploymentStatusAsync(requestId);
isReady = statusResponse.Data.CurrentStatus == EdgegapWindowMetadata.READY_STATUS;
}
return statusResponse;
}
/// <summary>If you recently stopped a deployment, but want to await TERMINATED (410) status.</summary>
/// <param name="requestId"></param>
/// <param name="pollInterval"></param>
public async Task<EdgegapHttpResult<StopActiveDeploymentResult>> AwaitTerminatedDeleteStatusAsync(
string requestId,
TimeSpan pollInterval)
{
EdgegapHttpResult<StopActiveDeploymentResult> deleteResponse = null;
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes( // MIRROR CHANGE: 'new()' not supported in Unity 2020
EdgegapWindowMetadata.DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS));
bool isStopped = false;
while (!isStopped && !cts.Token.IsCancellationRequested)
{
await Task.Delay(pollInterval, cts.Token);
deleteResponse = await StopActiveDeploymentAsync(requestId);
isStopped = deleteResponse.StatusCode == HttpStatusCode.Gone; // 410
}
return deleteResponse;
}
#endregion Chained API Methods
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37ecdc6abda4402419438f2284ef2d95
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,47 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Edgegap.Editor.Api.Models.Results;
namespace Edgegap.Editor.Api
{
/// <summary>
/// Wraps the v1/ip API endpoint: "IP Lookup" API.
/// - API Doc | https://docs.edgegap.com/api/#tag/IP-Lookup
/// </summary>
public class EdgegapIpApi : EdgegapApiBase
{
public EdgegapIpApi(
ApiEnvironment apiEnvironment,
string apiToken,
EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error)
: base(apiEnvironment, apiToken, logLevel)
{
}
#region API Methods
/// <summary>
/// GET to v1/app
/// - Retrieve your public IP address.
/// - API Doc | https://docs.edgegap.com/api/#tag/IP-Lookup/operation/IP
/// </summary>
/// <returns>
/// Http info with GetCreateAppResult data model
/// - Success: 200
/// - Fail: 409 (app already exists), 400 (reached limit)
/// </returns>
public async Task<EdgegapHttpResult<GetYourPublicIpResult>> GetYourPublicIp()
{
HttpResponseMessage response = await GetAsync("v1/ip");
EdgegapHttpResult<GetYourPublicIpResult> result = new EdgegapHttpResult<GetYourPublicIpResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
bool isSuccess = response.StatusCode == HttpStatusCode.OK; // 200
if (!isSuccess)
return result;
return result;
}
#endregion // API Methods
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fac4a7425623f39488af09d60549313e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using System.Net.Http;
using System.Threading.Tasks;
using Edgegap.Editor.Api.Models.Results;
using Newtonsoft.Json.Linq;
namespace Edgegap.Editor.Api
{
/// <summary>Wraps the v1/wizard API endpoint. Used for internal purposes.</summary>
public class EdgegapWizardApi : EdgegapApiBase
{
/// <summary>Extended path after the base uri</summary>
public EdgegapWizardApi(
ApiEnvironment apiEnvironment,
string apiToken,
EdgegapWindowMetadata.LogLevel logLevel = EdgegapWindowMetadata.LogLevel.Error)
: base(apiEnvironment, apiToken, logLevel)
{
}
#region API Methods
/// <summary>POST to v1/wizard/init-quick-start</summary>
/// <returns>
/// Http info with no explicit data model
/// - Success: 204 (no result model)
/// </returns>
public async Task<EdgegapHttpResult> InitQuickStart()
{
string json = new JObject { ["source"] = "unity" }.ToString();
HttpResponseMessage response = await PostAsync("v1/wizard/init-quick-start", json);
EdgegapHttpResult result = new EdgegapHttpResult(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
return result;
}
/// <summary>GET to v1/wizard/registry-credentials</summary>
/// <returns>
/// - Http info with GetRegistryCredentialsResult data model
/// - Success: 200
/// - Error: Likely if called before a successful InitQuickStart(),
/// or if called in a staging env. Soon, this will be available in production.
/// </returns>
public async Task<EdgegapHttpResult<GetRegistryCredentialsResult>> GetRegistryCredentials()
{
HttpResponseMessage response = await GetAsync("v1/wizard/registry-credentials");
EdgegapHttpResult<GetRegistryCredentialsResult> result = new EdgegapHttpResult<GetRegistryCredentialsResult>(response); // MIRROR CHANGE: 'new()' not supported in Unity 2020
return result;
}
#endregion // API Methods
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6986ee67361f0b45928ccd70c7ab12c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aed107c714fce71449ef56590221c567
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models
{
/// <summary>
/// Used in `UpdateAppVersionRequest`, `CreateAppVersionRequest`.
/// For GetDeploymentStatusResult, see DeploymentPortsData
/// </summary>
public class AppPortsData
{
/// <summary>1024~49151; Default 7770</summary>
[JsonProperty("port")]
public int Port { get; set; } = EdgegapWindowMetadata.PORT_DEFAULT;
/// <summary>Default "UDP"</summary>
[JsonProperty("protocol")]
public string ProtocolStr { get; set; } = EdgegapWindowMetadata.DEFAULT_PROTOCOL_TYPE.ToString();
[JsonProperty("to_check")]
public bool ToCheck { get; set; } = true;
[JsonProperty("tls_upgrade")]
public bool TlsUpgrade { get; set; }
[JsonProperty("name")]
public string PortName { get; set; } = "Game Port";
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6d4864ea3706574fb35920c6fab46fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models
{
/// <summary>Used in `GetDeploymentStatus`.</summary>
public class DeploymentPortsData
{
[JsonProperty("external")]
public int External { get; set; }
[JsonProperty("internal")]
public int Internal { get; set; }
[JsonProperty("protocol")]
public string Protocol { get; set; }
[JsonProperty("name")]
public string PortName { get; set; }
[JsonProperty("tls_upgrade")]
public bool TlsUpgrade { get; set; }
[JsonProperty("link")]
public string Link { get; set; }
[JsonProperty("proxy")]
public int? Proxy { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 62d51b44b8414c9f968ca607ccb06b7e
timeCreated: 1701522748

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models
{
public class LocationData
{
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("continent")]
public string Continent { get; set; }
[JsonProperty("administrative_division")]
public string AdministrativeDivision { get; set; }
[JsonProperty("timezone")]
public string Timezone { get; set; }
[JsonProperty("latitude")]
public double Latitude { get; set; }
[JsonProperty("longitude")]
public double Longitude { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57eed0dbd556e074c992cf6599a1f6bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
namespace Edgegap.Editor.Api.Models
{
/// <summary>
/// Unity default: UDP.
/// (!) UDP !works in WebGL.
/// </summary>
public enum ProtocolType
{
/// <summary>Unity default - fastest; !works in WebGL.</summary>
UDP,
/// <summary>Slower, but more reliable; works in WebGL.</summary>
TCP,
/// <summary>Slower, but more reliable; works in WebGL.</summary>
WS,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be5acd63e783b364ebdbb783639e2d32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1b2a5c481353934f906c30ba047df9b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Requests
{
/// <summary>
/// Request model for https://docs.edgegap.com/api/#tag/Applications/operation/application-post
/// </summary>
public class CreateAppRequest
{
#region Required
/// <summary>*The application name.</summary>
[JsonProperty("name")]
public string AppName { get; set; }
#endregion // Required
#region Optional
/// <summary>*If the application can be deployed.</summary>
[JsonProperty("is_active")]
public bool IsActive { get; set; }
/// <summary>*Image base64 string.</summary>
[JsonProperty("image")]
public string Image { get; set; }
/// <summary>If the telemetry agent is installed on the versions of this app.</summary>
[JsonProperty("is_telemetry_agent_active")]
public bool IsTelemetryAgentActive { get; set; }
#endregion // Optional
/// <summary>Used by Newtonsoft</summary>
public CreateAppRequest()
{
}
/// <summary>Init with required info</summary>
/// <param name="appName">The application name</param>
/// <param name="isActive">If the application can be deployed</param>
/// <param name="image">Image base64 string</param>
public CreateAppRequest(
string appName,
bool isActive,
string image)
{
this.AppName = appName;
this.IsActive = isActive;
this.Image = image;
}
/// <summary>Parse to json str</summary>
public override string ToString() =>
JsonConvert.SerializeObject(this);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a492d7c515b8894ea30b37db6b7efe4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,225 @@
using System;
using Newtonsoft.Json;
using UnityEngine;
namespace Edgegap.Editor.Api.Models.Requests
{
/// <summary>
/// Request model for `POST v1/app/{app_name}/version`.
/// API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-version-post
/// </summary>
public class CreateAppVersionRequest
{
#region Required
/// <summary>*The name of the application associated.</summary>
[JsonIgnore] // *Path var
public string AppName { get; set; }
/// <summary>*The name of the application associated.</summary>
[JsonProperty("name")]
public string VersionName { get; set; } = EdgegapWindowMetadata.DEFAULT_VERSION_TAG;
/// <summary>*The tag of your image. Default == "latest".</summary>
/// <example>"0.1.2" || "latest" (although "latest" !recommended; use actual versions in production)</example>
[JsonProperty("docker_tag")]
public string DockerTag { get; set; } = EdgegapWindowMetadata.DEFAULT_VERSION_TAG;
/// <summary>*The name of your image.</summary>
/// <example>"edgegap/demo" || "myCompany-someId/mylowercaseapp"</example>
[JsonProperty("docker_image")]
public string DockerImage { get; set; } = "";
/// <summary>*The Repository where the image is.</summary>
/// <example>"registry.edgegap.com" || "harbor.edgegap.com" || "docker.io"</example>
[JsonProperty("docker_repository")]
public string DockerRepository { get; set; } = "";
/// <summary>*Units of vCPU needed (1024 = 1vcpu)</summary>
[JsonProperty("req_cpu")]
public int ReqCpu { get; set; } = 256;
/// <summary>*Units of memory in MB needed (1024 = 1 GPU)</summary>
[JsonProperty("req_memory")]
public int ReqMemory { get; set; } = 256;
/// <summary>*Required: At least 1 { Port, ProtocolStr }.</summary>
[JsonProperty("ports")]
public AppPortsData[] Ports { get; set; } = {};
/// <summary>The username to access the docker repository</summary>
[JsonProperty("private_username")]
public string PrivateUsername { get; set; } = "";
/// <summary>The Private Password or Token of the username (We recommend to use a token)</summary>
[JsonProperty("private_token")]
public string PrivateToken { get; set; } = "";
#endregion // Required
// #region Optional
// [JsonProperty("is_active")]
// public bool IsActive { get; set; } = true;
//
// [JsonProperty("req_video")]
// public int ReqVideo { get; set; } = 256;
//
// [JsonProperty("max_duration")]
// public int MaxDuration { get; set; } = 30;
//
// [JsonProperty("use_telemetry")]
// public bool UseTelemetry { get; set; } = true;
//
// [JsonProperty("inject_context_env")]
// public bool InjectContextEnv { get; set; } = true;
//
// [JsonProperty("whitelisting_active")]
// public bool WhitelistingActive { get; set; } = true;
//
// [JsonProperty("force_cache")]
// public bool ForceCache { get; set; }
//
// [JsonProperty("cache_min_hour")]
// public int CacheMinHour { get; set; }
//
// [JsonProperty("cache_max_hour")]
// public int CacheMaxHour { get; set; }
//
// [JsonProperty("time_to_deploy")]
// public int TimeToDeploy { get; set; } = 15;
//
// [JsonProperty("enable_all_locations")]
// public bool EnableAllLocations { get; set; }
//
// [JsonProperty("termination_grace_period_seconds")]
// public int TerminationGracePeriodSeconds { get; set; } = 5;
//
// [JsonProperty("endpoint_storage")]
// public string EndpointStorage { get; set; } = "";
//
// [JsonProperty("command")]
// public string Command { get; set; }
//
// [JsonProperty("arguments")]
// public string Arguments { get; set; }
//
// [JsonProperty("verify_image")]
// public bool VerifyImage { get; set; }
//
// [JsonProperty("session_config")]
// public SessionConfigData SessionConfig { get; set; } = new();
//
// [JsonProperty("probe")]
// public ProbeData Probe { get; set; } = new();
//
// [JsonProperty("envs")]
// public EnvsData[] Envs { get; set; } = {};
//
// public class SessionConfigData
// {
// [JsonProperty("kind")]
// public string Kind { get; set; } = "Seat";
//
// [JsonProperty("sockets")]
// public int Sockets { get; set; } = 10;
//
// [JsonProperty("autodeploy")]
// public bool Autodeploy { get; set; } = true;
//
// [JsonProperty("empty_ttl")]
// public int EmptyTtl { get; set; } = 60;
//
// [JsonProperty("session_max_duration")]
// public int SessionMaxDuration { get; set; } = 60;
// }
//
//
// public class ProbeData
// {
// [JsonProperty("optimal_ping")]
// public int OptimalPing { get; set; } = 60;
//
// [JsonProperty("rejected_ping")]
// public int RejectedPing { get; set; } = 180;
// }
//
// public class EnvsData
// {
// [JsonProperty("key")]
// public string Key { get; set; }
//
// [JsonProperty("value")]
// public string Value { get; set; }
//
// [JsonProperty("is_secret")]
// public bool IsSecret { get; set; } = true;
// }
// #endregion // Optional
/// <summary>Used by Newtonsoft</summary>
public CreateAppVersionRequest()
{
}
/// <summary>
/// Init with required info.
/// (!) If looking for refs, also see FromUpdateRequest() builder below.
/// </summary>
/// <param name="appName">The name of the application.</param>
/// <param name="containerRegistryUsername"></param>
/// <param name="containerRegistryPasswordToken"></param>
/// <param name="portNum"></param>
/// <param name="protocolType"></param>
public CreateAppVersionRequest(
string appName,
string containerRegistryUsername,
string containerRegistryPasswordToken,
int portNum,
ProtocolType protocolType)
{
this.AppName = appName;
this.PrivateUsername = containerRegistryUsername;
this.PrivateToken = containerRegistryPasswordToken;
this.Ports = new AppPortsData[]
{
new AppPortsData() // MIRROR CHANGE: 'new()' not supported in Unity 2020
{
Port = portNum,
ProtocolStr = protocolType.ToString(),
},
};
}
/// <summary>
/// Port from Update PATCH model: If you tried to Update, but !exists, you probably want to create it next.
/// </summary>
/// <param name="updateRequest"></param>
public static CreateAppVersionRequest FromUpdateRequest(UpdateAppVersionRequest updateRequest)
{
// Convert the updateRequest to JSON
string json = JsonConvert.SerializeObject(updateRequest);
// Deserialize the JSON back to CreateAppVersionRequest
CreateAppVersionRequest createReq = null;
try
{
createReq = JsonConvert.DeserializeObject<CreateAppVersionRequest>(json);
createReq.AppName = updateRequest.AppName; // Normally JsonIgnored in Update
createReq.VersionName = updateRequest.VersionName; // Normally JsonIgnored in Update
createReq.PrivateUsername = updateRequest.PrivateUsername;
createReq.PrivateToken = updateRequest.PrivateToken;
}
catch (Exception e)
{
Debug.LogError($"Error (when parsing CreateAppVersionRequest from CreateAppVersionRequest): {e}");
throw;
}
return createReq;
}
/// <summary>Parse to json str</summary>
public override string ToString() =>
JsonConvert.SerializeObject(this);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bb645e2f9d04384a85739269cc8a4e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Requests
{
/// <summary>
/// Request model for `POST v1/deploy`.
/// API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deploy
/// </summary>
public class CreateDeploymentRequest
{
#region Required
/// <summary>*Required: The name of the App you want to deploy.</summary>
[JsonProperty("app_name")]
public string AppName { get; set; }
/// <summary>
/// *Required: The name of the App Version you want to deploy;
/// if not present, the last version created is picked.
/// </summary>
[JsonProperty("version_name")]
public string VersionName { get; set; }
/// <summary>
/// *Required: The List of IP of your user.
/// </summary>
[JsonProperty("ip_list")]
public string[] IpList { get; set; }
/// <summary>
/// *Required: The list of IP of your user with their location (latitude, longitude).
/// </summary>
[JsonProperty("geo_ip_list")]
public string[] GeoIpList { get; set; } = {};
#endregion // Required
/// <summary>Used by Newtonsoft</summary>
public CreateDeploymentRequest()
{
}
/// <summary>Init with required info; used for a single external IP address.</summary>
/// <param name="appName">The name of the application.</param>
/// <param name="versionName">
/// The name of the App Version you want to deploy, if not present,
/// the last version created is picked.
/// </param>
/// <param name="externalIp">Obtain from IpApi.</param>
public CreateDeploymentRequest(
string appName,
string versionName,
string externalIp)
{
this.AppName = appName;
this.VersionName = versionName;
this.IpList = new[] { externalIp };
}
/// <summary>Parse to json str</summary>
public override string ToString() =>
JsonConvert.SerializeObject(this);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aae7b317093230e419bc0f8be1097ea6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,175 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Requests
{
/// <summary>
/// Request model for `PATCH v1/app/{app_name}/version/{version_name}`.
/// Request model for https://docs.edgegap.com/api/#tag/Applications/operation/app-versions-patch
/// TODO: Split "Create" and "Update" into their own, separate models: CTRL+F for "(!)" for more info.
/// </summary>
public class UpdateAppVersionRequest
{
#region Required
/// <summary>*Required: The name of the application.</summary>
[JsonIgnore] // *Path var
public string AppName { get; set; }
#endregion // Required
#region Optional
/// <summary>The name of the application version.</summary>
[JsonIgnore] // *Path var
public string VersionName { get; set; } = EdgegapWindowMetadata.DEFAULT_VERSION_TAG;
/// <summary>At least 1 { Port, ProtocolStr }</summary>
[JsonProperty("ports")]
public AppPortsData[] Ports { get; set; } = {};
/// <summary>The Repository where the image is.</summary>
/// <example>"registry.edgegap.com" || "harbor.edgegap.com" || "docker.io"</example>
[JsonProperty("docker_repository")]
public string DockerRepository { get; set; } = "";
/// <summary>The name of your image.</summary>
/// <example>"edgegap/demo" || "myCompany-someId/mylowercaseapp"</example>
[JsonProperty("docker_image")]
public string DockerImage { get; set; } = "";
/// <summary>The tag of your image. Default == "latest".</summary>
/// <example>"0.1.2" || "latest" (although "latest" !recommended; use actual versions in production)</example>
[JsonProperty("docker_tag")]
public string DockerTag { get; set; } = EdgegapWindowMetadata.DEFAULT_VERSION_TAG;
[JsonProperty("is_active")]
public bool IsActive { get; set; } = true;
[JsonProperty("private_username")]
public string PrivateUsername { get; set; } = "";
[JsonProperty("private_token")]
public string PrivateToken { get; set; } = "";
#region (!) Shows in API docs for PATCH, but could be CREATE only? "Unknown Args"
// [JsonProperty("req_cpu")]
// public int ReqCpu { get; set; } = 256;
//
// [JsonProperty("req_memory")]
// public int ReqMemory { get; set; } = 256;
//
// [JsonProperty("req_video")]
// public int ReqVideo { get; set; } = 256;
#endregion // (!) Shows in API docs for PATCH, but could be CREATE only? "Unknown Args"
[JsonProperty("max_duration")]
public int MaxDuration { get; set; } = 60;
[JsonProperty("use_telemetry")]
public bool UseTelemetry { get; set; } = true;
[JsonProperty("inject_context_env")]
public bool InjectContextEnv { get; set; } = true;
[JsonProperty("whitelisting_active")]
public bool WhitelistingActive { get; set; } = false;
[JsonProperty("force_cache")]
public bool ForceCache { get; set; }
[JsonProperty("cache_min_hour")]
public int CacheMinHour { get; set; }
[JsonProperty("cache_max_hour")]
public int CacheMaxHour { get; set; }
[JsonProperty("time_to_deploy")]
public int TimeToDeploy { get; set; } = 120;
[JsonProperty("enable_all_locations")]
public bool EnableAllLocations { get; set; }
[JsonProperty("termination_grace_period_seconds")]
public int TerminationGracePeriodSeconds { get; set; } = 5;
// // (!) BUG: Expects empty string "" at minimum; however, empty string will throw server err
// [JsonProperty("endpoint_storage")]
// public string EndpointStorage { get; set; }
[JsonProperty("command")]
public string Command { get; set; }
[JsonProperty("arguments")]
public string Arguments { get; set; }
// /// <summary>
// /// (!) Setting this will trigger a very specific type of game that will affect the AppVersion.
// /// TODO: Is leaving as null the same as commenting out?
// /// </summary>
// [JsonProperty("session_config")]
// public SessionConfigData SessionConfig { get; set; }
[JsonProperty("probe")]
public ProbeData Probe { get; set; } = new ProbeData(); // MIRROR CHANGE: 'new()' not supported in Unity 2020
[JsonProperty("envs")]
public EnvsData[] Envs { get; set; } = {};
public class SessionConfigData
{
[JsonProperty("kind")]
public string Kind { get; set; } = "Seat";
[JsonProperty("sockets")]
public int Sockets { get; set; } = 10;
[JsonProperty("autodeploy")]
public bool Autodeploy { get; set; } = true;
[JsonProperty("empty_ttl")]
public int EmptyTtl { get; set; } = 60;
[JsonProperty("session_max_duration")]
public int SessionMaxDuration { get; set; } = 60;
}
public class ProbeData
{
[JsonProperty("optimal_ping")]
public int OptimalPing { get; set; } = 60;
[JsonProperty("rejected_ping")]
public int RejectedPing { get; set; } = 180;
}
public class EnvsData
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
[JsonProperty("is_secret")]
public bool IsSecret { get; set; } = true;
}
#endregion // Optional
/// <summary>Used by Newtonsoft</summary>
public UpdateAppVersionRequest()
{
}
/// <summary>
/// Init with required info. Default version/tag == "default".
/// Since we're updating, we only require the AppName.
/// </summary>
/// <param name="appName">The name of the application.</param>
public UpdateAppVersionRequest(string appName)
{
this.AppName = appName;
}
/// <summary>Parse to json str</summary>
public override string ToString() =>
JsonConvert.SerializeObject(this);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8da9712633ee1e64faca0b960d4bed31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa4ceffbc97b8254885a63937def2324
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for `POST v1/deploy`.
/// </summary>
public class CreateDeploymentResult
{
[JsonProperty("request_id")]
public string RequestId { get; set; }
[JsonProperty("request_dns")]
public string RequestDns { get; set; }
[JsonProperty("request_app")]
public string RequestApp { get; set; }
[JsonProperty("request_version")]
public string RequestVersion { get; set; }
[JsonProperty("request_user_count")]
public int RequestUserCount { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("continent")]
public string Continent { get; set; }
[JsonProperty("administrative_division")]
public string AdministrativeDivision { get; set; }
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("container_log_storage")]
public ContainerLogStorageData ContainerLogStorage { get; set; }
public class ContainerLogStorageData
{
[JsonProperty("enabled")]
public bool Enabled { get; set; }
[JsonProperty("endpoint_storage")]
public string EndpointStorage { get; set; }
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8361abc6f84fccd4cba26dc285d335dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>Edgegap error, generally just containing `message`</summary>
public class EdgegapErrorResult
{
/// <summary>Friendly, UI-facing error message from Edgegap; can be lengthy.</summary>
[JsonProperty("message")]
public string ErrorMessage { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b29093cb10cf3040b76f4fbe77a435d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
using System;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Wraps the inner json data with outer http info.
/// This class overload contains no json-deserialiable data result.
/// </summary>
public class EdgegapHttpResult
{
/// <summary>HTTP Status code for the request.</summary>
public HttpStatusCode StatusCode { get; }
/// <summary>This could be err, success, or null.</summary>
public string Json { get; }
/// <summary>eg: "POST"</summary>
public HttpMethod HttpMethod;
/// <summary>
/// Typically is sent by servers together with the status code.
/// Useful for fallback err descriptions, often based on the status code.
/// </summary>
public string ReasonPhrase { get; }
/// <summary>Contains `message` with friendly info.</summary>
public bool HasErr => Error != null;
public EdgegapErrorResult Error { get; set; }
#region Common Shortcuts
/// <summary>OK</summary>
public bool IsResultCode200 => StatusCode == HttpStatusCode.OK;
/// <summary>NoContent</summary>
public bool IsResultCode204 => StatusCode == HttpStatusCode.NoContent;
/// <summary>Forbidden</summary>
public bool IsResultCode403 => StatusCode == HttpStatusCode.Forbidden;
/// <summary>Conflict</summary>
public bool IsResultCode409 => StatusCode == HttpStatusCode.Conflict;
/// <summary>BadRequest</summary>
public bool IsResultCode400 => StatusCode == HttpStatusCode.BadRequest;
/// <summary>Gone</summary>
public bool IsResultCode410 => StatusCode == HttpStatusCode.Gone;
#endregion // Common Shortcuts
/// <summary>
/// Constructor that initializes the class based on an HttpResponseMessage.
/// </summary>
public EdgegapHttpResult(HttpResponseMessage httpResponse)
{
this.ReasonPhrase = httpResponse.ReasonPhrase;
this.StatusCode = httpResponse.StatusCode;
try
{
// TODO: This can be read async with `await`, but can't do this in a Constructor.
// Instead, make a factory builder Task =>
this.Json = httpResponse.Content.ReadAsStringAsync().Result;
this.Error = JsonConvert.DeserializeObject<EdgegapErrorResult>(Json);
if (Error != null && string.IsNullOrEmpty(Error.ErrorMessage))
Error = null;
}
catch (Exception e)
{
Debug.LogError("Error (reading httpResponse.Content): Client expected json, " +
$"but server returned !json: {e} - ");
}
}
}
/// <summary>
/// Wraps the inner json data with outer http info.
/// This class overload contains json-deserialiable data result.
/// </summary>
public class EdgegapHttpResult<TResult> : EdgegapHttpResult
{
/// <summary>The actual result model from Json. Could be null!</summary>
public TResult Data { get; set; }
public EdgegapHttpResult(HttpResponseMessage httpResponse, bool isLogLevelDebug = false)
: base(httpResponse)
{
this.HttpMethod = httpResponse.RequestMessage.Method;
// Assuming JSON content and using Newtonsoft.Json for deserialization
bool isDeserializable = httpResponse.Content != null &&
httpResponse.Content.Headers.ContentType.MediaType == "application/json";
if (isDeserializable)
{
try
{
this.Data = JsonConvert.DeserializeObject<TResult>(Json);
}
catch (Exception e)
{
Debug.LogError($"Error (deserializing EdgegapHttpResult.Data): {e} - json: {Json}");
throw;
}
}
if (isLogLevelDebug)
UnityEngine.Debug.Log($"{typeof(TResult).Name} result: {JObject.Parse(Json)}"); // Prettified
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 888bfc2c113487b44a3103648d2c2ae3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for `[GET | POST] v1/app`.
/// POST API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/application-post
/// GET API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/application-get
/// </summary>
public class GetCreateAppResult
{
[JsonProperty("name")]
public string AppName { get; set; }
[JsonProperty("is_active")]
public bool IsActive { get; set; }
/// <summary>Optional</summary>
[JsonProperty("is_telemetry_agent_active")]
public bool IsTelemetryAgentActive { get; set; }
[JsonProperty("image")]
public string Image { get; set; }
[JsonProperty("create_time")]
public string CreateTimeStr { get; set; }
[JsonProperty("last_updated")]
public string LastUpdatedStr { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a66c935238edd8846b1e9e9e19cfab70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for `GET v1/status/{request_id}`.
/// API Doc | https://docs.edgegap.com/api/#tag/Deployments/operation/deployment-status-get
/// </summary>
public class GetDeploymentStatusResult
{
[JsonProperty("request_id")]
public string RequestId { get; set; }
[JsonProperty("fqdn")]
public string Fqdn { get; set; }
[JsonProperty("app_name")]
public string AppName { get; set; }
[JsonProperty("app_version")]
public string AppVersion { get; set; }
[JsonProperty("current_status")]
public string CurrentStatus { get; set; }
[JsonProperty("running")]
public bool Running { get; set; }
[JsonProperty("whitelisting_active")]
public bool WhitelistingActive { get; set; }
[JsonProperty("start_time")]
public string StartTime { get; set; }
[JsonProperty("removal_time")]
public string RemovalTime { get; set; }
[JsonProperty("elapsed_time")]
public int? ElapsedTime { get; set; }
[JsonProperty("last_status")]
public string LastStatus { get; set; }
[JsonProperty("error")]
public bool Error { get; set; }
[JsonProperty("error_detail")]
public string ErrorDetail { get; set; }
[JsonProperty("public_ip")]
public string PublicIp { get; set; }
[JsonProperty("sessions")]
public SessionData[] Sessions { get; set; }
[JsonProperty("location")]
public LocationData Location { get; set; }
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("sockets")]
public string Sockets { get; set; }
[JsonProperty("sockets_usage")]
public string SocketsUsage { get; set; }
[JsonProperty("command")]
public string Command { get; set; }
[JsonProperty("arguments")]
public string Arguments { get; set; }
/// <summary>
/// TODO: Server should swap `ports` to an array of DeploymentPortsData (instead of an object of dynamic unknown objects).
/// <example>
/// {
/// "7777", {}
/// },
/// {
/// "Some Port Name", {}
/// }
/// </example>
/// </summary>
[JsonProperty("ports")]
public Dictionary<string, DeploymentPortsData> PortsDict { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c658b7f5c5d5d0648934b0ae1d71de9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for `GET v1/wizard/registry-credentials`.
/// </summary>
public class GetRegistryCredentialsResult
{
[JsonProperty("registry_url")]
public string RegistryUrl { get; set; }
[JsonProperty("project")]
public string Project { get; set; }
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("token")]
public string Token { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e6af130c329d2b43b2f4b0dc8639477
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for `GET v1/ip`.
/// GET API Doc | https://docs.edgegap.com/api/#tag/IP-Lookup/operation/IP
/// </summary>
public class GetYourPublicIpResult
{
[JsonProperty("public_ip")]
public string PublicIp { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73c9651ef0fdfcb449ec0120016963a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
public class StopActiveDeploymentResult
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("deployment_summary")]
public DeploymentSummaryData DeploymentSummary { get; set; }
public class DeploymentSummaryData
{
[JsonProperty("request_id")]
public string RequestId { get; set; }
[JsonProperty("fqdn")]
public string Fqdn { get; set; }
[JsonProperty("app_name")]
public string AppName { get; set; }
[JsonProperty("app_version")]
public string AppVersion { get; set; }
[JsonProperty("current_status")]
public string CurrentStatus { get; set; }
[JsonProperty("running")]
public bool Running { get; set; }
[JsonProperty("whitelisting_active")]
public bool WhitelistingActive { get; set; }
[JsonProperty("start_time")]
public string StartTime { get; set; }
[JsonProperty("removal_time")]
public string RemovalTime { get; set; }
[JsonProperty("elapsed_time")]
public int ElapsedTime { get; set; }
[JsonProperty("last_status")]
public string LastStatus { get; set; }
[JsonProperty("error")]
public bool Error { get; set; }
[JsonProperty("error_detail")]
public string ErrorDetail { get; set; }
[JsonProperty("ports")]
public PortsData Ports { get; set; }
[JsonProperty("public_ip")]
public string PublicIp { get; set; }
[JsonProperty("sessions")]
public SessionData[] Sessions { get; set; }
[JsonProperty("location")]
public LocationData Location { get; set; }
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("sockets")]
public string Sockets { get; set; }
[JsonProperty("sockets_usage")]
public string SocketsUsage { get; set; }
[JsonProperty("command")]
public string Command { get; set; }
[JsonProperty("arguments")]
public string Arguments { get; set; }
}
public class PortsData
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e2e4d9424ca8f7459803e631acf912f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,165 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models.Results
{
/// <summary>
/// Result model for:
/// - `POST 1/app/{app_name}/version`
/// - `PATCH v1/app/{app_name}/version/{version_name}`
/// POST API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/application-post
/// PATCH API Doc | https://docs.edgegap.com/api/#tag/Applications/operation/app-versions-patch
/// </summary>
public class UpsertAppVersionResult
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("version")]
public VersionData Version { get; set; }
public class VersionData
{
[JsonProperty("name")]
public string VersionName { get; set; }
[JsonProperty("is_active")]
public bool IsActive { get; set; }
[JsonProperty("docker_repository")]
public string DockerRepository { get; set; }
[JsonProperty("docker_image")]
public string DockerImage { get; set; }
[JsonProperty("docker_tag")]
public string DockerTag { get; set; }
[JsonProperty("private_username")]
public string PrivateUsername { get; set; }
[JsonProperty("private_token")]
public string PrivateToken { get; set; }
[JsonProperty("req_cpu")]
public int? ReqCpu { get; set; }
[JsonProperty("req_memory")]
public int? ReqMemory { get; set; }
[JsonProperty("req_video")]
public int? ReqVideo { get; set; }
[JsonProperty("max_duration")]
public int? MaxDuration { get; set; }
[JsonProperty("use_telemetry")]
public bool UseTelemetry { get; set; }
[JsonProperty("inject_context_env")]
public bool InjectContextEnv { get; set; }
[JsonProperty("whitelisting_active")]
public bool WhitelistingActive { get; set; }
[JsonProperty("force_cache")]
public bool ForceCache { get; set; }
[JsonProperty("cache_min_hour")]
public int? CacheMinHour { get; set; }
[JsonProperty("cache_max_hour")]
public int? CacheMaxHour { get; set; }
[JsonProperty("time_to_deploy")]
public int? TimeToDeploy { get; set; }
[JsonProperty("enable_all_locations")]
public bool EnableAllLocations { get; set; }
[JsonProperty("session_config")]
public SessionConfigData SessionConfig { get; set; }
[JsonProperty("ports")]
public PortsData[] Ports { get; set; }
[JsonProperty("probe")]
public ProbeData Probe { get; set; }
[JsonProperty("envs")]
public EnvsData[] Envs { get; set; }
[JsonProperty("verify_image")]
public bool VerifyImage { get; set; }
[JsonProperty("termination_grace_period_seconds")]
public int? TerminationGracePeriodSeconds { get; set; }
[JsonProperty("endpoint_storage")]
public string EndpointStorage { get; set; }
[JsonProperty("command")]
public string Command { get; set; }
[JsonProperty("arguments")]
public string Arguments { get; set; }
}
public class SessionConfigData
{
[JsonProperty("kind")]
public string Kind { get; set; }
[JsonProperty("sockets")]
public int? Sockets { get; set; }
[JsonProperty("autodeploy")]
public bool Autodeploy { get; set; }
[JsonProperty("empty_ttl")]
public int? EmptyTtl { get; set; }
[JsonProperty("session_max_duration")]
public int? SessionMaxDuration { get; set; }
}
public class PortsData
{
[JsonProperty("port")]
public int? Port { get; set; }
[JsonProperty("protocol")]
public string Protocol { get; set; }
[JsonProperty("to_check")]
public bool ToCheck { get; set; }
[JsonProperty("tls_upgrade")]
public bool TlsUpgrade { get; set; }
[JsonProperty("name")]
public string PortName { get; set; }
}
public class ProbeData
{
[JsonProperty("optimal_ping")]
public int? OptimalPing { get; set; }
[JsonProperty("rejected_ping")]
public int? RejectedPing { get; set; }
}
public class EnvsData
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
[JsonProperty("is_secret")]
public bool IsSecret { get; set; }
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7dde7d59c66d8c44b86af35e853f9f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
namespace Edgegap.Editor.Api.Models
{
/// <summary>
/// Shared model for `GetDeploymentStatusResult`, `StopActiveDeploymentResult`.
/// </summary>
public class SessionData
{
[JsonProperty("session_id")]
public string SessionId { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("ready")]
public bool Ready { get; set; }
[JsonProperty("linked")]
public bool Linked { get; set; }
[JsonProperty("kind")]
public string Kind { get; set; }
[JsonProperty("user_count")]
public string UserCount { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f9024e4ca5438e4788e461387313531
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
using System.Threading.Tasks;
using UnityEngine.UIElements;
namespace Edgegap.Editor
{
/// <summary>Slightly shake a UI button to indicate attention.</summary>
public class ButtonShaker
{
const string SHAKE_START_CLASS = "shakeStart";
const string SHAKE_STOP_CLASS = "shakeEnd";
private Button targetButton;
public ButtonShaker(Button buttonToShake) =>
targetButton = buttonToShake;
/// <summary>Shake the button x times for x msDelayBetweenShakes each.
/// 1 shake = 1 bigger -> followed by 1 smaller.</summary>
/// <param name="msDelayBetweenShakes"></param>
/// <param name="iterations"># of shakes</param>
public async Task ApplyShakeAsync(int msDelayBetweenShakes = 40, int iterations = 2)
{
for (int i = 0; i < iterations; i++)
await shakeOnce(msDelayBetweenShakes);
}
private async Task shakeOnce(int msDelayBetweenShakes)
{
targetButton.AddToClassList(SHAKE_START_CLASS);
await Task.Delay(msDelayBetweenShakes); // duration of the first transition
targetButton.RemoveFromClassList(SHAKE_START_CLASS);
targetButton.AddToClassList(SHAKE_STOP_CLASS);
await Task.Delay(msDelayBetweenShakes); // duration of the second transition
targetButton.RemoveFromClassList(SHAKE_STOP_CLASS);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f963d64ffcf32ba4bba54fe1cd70b5a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,307 @@
#if UNITY_EDITOR
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Build.Reporting;
using Debug = UnityEngine.Debug;
namespace Edgegap
{
internal static class EdgegapBuildUtils
{
public static bool IsArmCPU() =>
RuntimeInformation.ProcessArchitecture == Architecture.Arm ||
RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
public static BuildReport BuildServer()
{
IEnumerable<string> scenes = EditorBuildSettings.scenes
.Where(s => s.enabled)
.Select(s => s.path);
BuildPlayerOptions options = new BuildPlayerOptions
{
scenes = scenes.ToArray(),
target = BuildTarget.StandaloneLinux64,
// MIRROR CHANGE
#if UNITY_2021_3_OR_NEWER
subtarget = (int)StandaloneBuildSubtarget.Server, // dedicated server with UNITY_SERVER define
#else
options = BuildOptions.EnableHeadlessMode, // obsolete and missing UNITY_SERVER define
#endif
// END MIRROR CHANGE
locationPathName = "Builds/EdgegapServer/ServerBuild"
};
return BuildPipeline.BuildPlayer(options);
}
public static async Task<bool> DockerSetupAndInstallationCheck()
{
if (!File.Exists("Dockerfile"))
{
File.WriteAllText("Dockerfile", dockerFileText);
}
string output = null;
string error = null;
await RunCommand_DockerVersion(msg => output = msg, msg => error = msg); // MIRROR CHANGE
if (!string.IsNullOrEmpty(error))
{
Debug.LogError(error);
return false;
}
Debug.Log($"[Edgegap] Docker version detected: {output}"); // MIRROR CHANGE
return true;
}
// MIRROR CHANGE
static async Task RunCommand_DockerVersion(Action<string> outputReciever = null, Action<string> errorReciever = null)
{
#if UNITY_EDITOR_WIN
await RunCommand("cmd.exe", "/c docker --version", outputReciever, errorReciever);
#elif UNITY_EDITOR_OSX
await RunCommand("/bin/bash", "-c \"docker --version\"", outputReciever, errorReciever);
#elif UNITY_EDITOR_LINUX
await RunCommand("/bin/bash", "-c \"docker --version\"", outputReciever, errorReciever);
#else
Debug.LogError("The platform is not supported yet.");
#endif
}
// MIRROR CHANGE
public static async Task RunCommand_DockerBuild(string registry, string imageRepo, string tag, Action<string> onStatusUpdate)
{
string realErrorMessage = null;
// ARM -> x86 support:
// build commands use 'buildx' on ARM cpus for cross compilation.
// otherwise docker builds would not launch when deployed because
// Edgegap's infrastructure is on x86. instead the deployment logs
// would show an error in a linux .go file with 'not found'.
string buildCommand = IsArmCPU() ? "buildx build --platform linux/amd64" : "build";
#if UNITY_EDITOR_WIN
await RunCommand("docker.exe", $"{buildCommand} -t {registry}/{imageRepo}:{tag} .", onStatusUpdate,
#elif UNITY_EDITOR_OSX
await RunCommand("/bin/bash", $"-c \"docker {buildCommand} -t {registry}/{imageRepo}:{tag} .\"", onStatusUpdate,
#elif UNITY_EDITOR_LINUX
await RunCommand("/bin/bash", $"-c \"docker {buildCommand} -t {registry}/{imageRepo}:{tag} .\"", onStatusUpdate,
#endif
(msg) =>
{
if (msg.Contains("ERROR"))
{
realErrorMessage = msg;
}
onStatusUpdate(msg);
});
if(realErrorMessage != null)
{
throw new Exception(realErrorMessage);
}
}
public static async Task<(bool, string)> RunCommand_DockerPush(string registry, string imageRepo, string tag, Action<string> onStatusUpdate)
{
string error = string.Empty;
#if UNITY_EDITOR_WIN
await RunCommand("docker.exe", $"push {registry}/{imageRepo}:{tag}", onStatusUpdate, (msg) => error += msg + "\n");
#elif UNITY_EDITOR_OSX
await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, (msg) => error += msg + "\n");
#elif UNITY_EDITOR_LINUX
await RunCommand("/bin/bash", $"-c \"docker push {registry}/{imageRepo}:{tag}\"", onStatusUpdate, (msg) => error += msg + "\n");
#endif
if (!string.IsNullOrEmpty(error))
{
Debug.LogError(error);
return (false, error);
}
return (true, null);
}
// END MIRROR CHANGE
static async Task RunCommand(string command, string arguments, Action<string> outputReciever = null, Action<string> errorReciever = null)
{
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = command,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
// MIRROR CHANGE
#if !UNITY_EDITOR_WIN
// on mac, commands like 'docker' aren't found because it's not in the application's PATH
// even if it runs on mac's terminal.
// to solve this we need to do two steps:
// 1. add /usr/bin/local to PATH if it's not there already. often this is missing in the application.
// this is where docker is usually instaled.
// 2. add PATH to ProcessStartInfo
string existingPath = Environment.GetEnvironmentVariable("PATH");
string customPath = $"{existingPath}:/usr/local/bin";
startInfo.EnvironmentVariables["PATH"] = customPath;
// Debug.Log("PATH: " + customPath);
#endif
// END MIRROR CHANGE
Process proc = new Process() { StartInfo = startInfo, };
proc.EnableRaisingEvents = true;
ConcurrentQueue<string> errors = new ConcurrentQueue<string>();
ConcurrentQueue<string> outputs = new ConcurrentQueue<string>();
void pipeQueue(ConcurrentQueue<string> q, Action<string> opt)
{
while (!q.IsEmpty)
{
if (q.TryDequeue(out string msg) && !string.IsNullOrWhiteSpace(msg))
{
opt?.Invoke(msg);
}
}
}
proc.OutputDataReceived += (s, e) => outputs.Enqueue(e.Data);
proc.ErrorDataReceived += (s, e) => errors.Enqueue(e.Data);
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
while (!proc.HasExited)
{
await Task.Delay(100);
pipeQueue(errors, errorReciever);
pipeQueue(outputs, outputReciever);
}
pipeQueue(errors, errorReciever);
pipeQueue(outputs, outputReciever);
}
static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
throw new NotImplementedException();
}
static Regex lastDigitsRegex = new Regex("([0-9])+$");
public static string IncrementTag(string tag)
{
Match lastDigits = lastDigitsRegex.Match(tag);
if (!lastDigits.Success)
{
return tag + " _1";
}
int number = int.Parse(lastDigits.Groups[0].Value);
number++;
return lastDigitsRegex.Replace(tag, number.ToString());
}
public static void UpdateEdgegapAppTag(string tag)
{
// throw new NotImplementedException();
}
// -batchmode -nographics remains for Unity 2019/2020 support pre-dedicated server builds
static string dockerFileText = @"FROM ubuntu:bionic
ARG DEBIAN_FRONTEND=noninteractive
COPY Builds/EdgegapServer /root/build/
WORKDIR /root/
RUN chmod +x /root/build/ServerBuild
RUN apt-get update && \
apt-get install -y ca-certificates && \
apt-get clean && \
update-ca-certificates
ENTRYPOINT [ ""/root/build/ServerBuild"", ""-batchmode"", ""-nographics""]
";
/// <summary>Run a Docker cmd with streaming log response. TODO: Plugin to other Docker cmds</summary>
/// <returns>Throws if logs contain "ERROR"</returns>
///
/// <param name="registryUrl">ex: "registry.edgegap.com"</param>
/// <param name="repoUsername">ex: "robot$mycompany-asdf+client-push"</param>
/// <param name="repoPasswordToken">Different from ApiToken; sometimes called "Container Registry Password"</param>
/// <param name="onStatusUpdate">Log stream</param>
// MIRROR CHANGE: CROSS PLATFORM SUPPORT
static async Task<bool> RunCommand_DockerLogin(
string registryUrl,
string repoUsername,
string repoPasswordToken,
Action<string> outputReciever = null, Action<string> errorReciever = null)
{
// TODO: Use --password-stdin for security (!) This is no easy task for child Process | https://stackoverflow.com/q/51489359/6541639
// (!) Don't use single quotes for cross-platform support (works unexpectedly in `cmd`).
try
{
#if UNITY_EDITOR_WIN
await RunCommand("cmd.exe", $"/c docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"", outputReciever, errorReciever);
#elif UNITY_EDITOR_OSX
await RunCommand("/bin/bash", $"-c \"docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"\"", outputReciever, errorReciever);
#elif UNITY_EDITOR_LINUX
await RunCommand("/bin/bash", $"-c \"docker login -u \"{repoUsername}\" --password \"{repoPasswordToken}\" \"{registryUrl}\"\"", outputReciever, errorReciever);
#else
Debug.LogError("The platform is not supported yet.");
#endif
}
catch (Exception e)
{
Debug.LogError($"Error: {e}");
return false;
}
return true;
}
/// <summary>
/// v2: Login to Docker Registry via RunCommand(), returning streamed log messages:
/// "docker login {registryUrl} {repository} {repoUsername} {repoPasswordToken}"
/// </summary>
/// <param name="registryUrl">ex: "registry.edgegap.com"</param>
/// <param name="repoUsername">ex: "robot$mycompany-asdf+client-push"</param>
/// <param name="repoPasswordToken">Different from ApiToken; sometimes called "Container Registry Password"</param>
/// <param name="onStatusUpdate">Log stream</param>
/// <returns>isSuccess</returns>
public static async Task<bool> LoginContainerRegistry(
string registryUrl,
string repoUsername,
string repoPasswordToken,
Action<string> onStatusUpdate)
{
string error = null;
await RunCommand_DockerLogin(registryUrl, repoUsername, repoPasswordToken, onStatusUpdate, msg => error = msg); // MIRROR CHANGE
if (error.Contains("ERROR"))
{
Debug.LogError(error);
return false;
}
return true;
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97dadab2a073d8b47bf9a270401f0a8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.UIElements;
using Edgegap;
[CustomEditor(typeof(EdgegapToolScript))]
public class EdgegapPluginScriptEditor : Editor
{
VisualElement _serverDataContainer;
private void OnEnable()
{
_serverDataContainer = EdgegapServerDataManager.GetServerDataVisualTree();
EdgegapServerDataManager.RegisterServerDataContainer(_serverDataContainer);
}
private void OnDisable()
{
EdgegapServerDataManager.DeregisterServerDataContainer(_serverDataContainer);
}
public override VisualElement CreateInspectorGUI()
{
return _serverDataContainer;
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4c676ae6dcca0e458c6a8f06571f8fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
.row__port-table {
padding: 2px 4px;
display: flex;
flex-direction: row;
align-items: center;
width: auto;
justify-content: space-around;
-unity-text-align: middle-left;
}
.row__port-table > * {
width: 0px;
flex-grow: 1;
align-self: center;
margin: 0px;
padding: 0px;
}
.focusable:hover {
background-color: rgba(0,0,0,0.2);
border-radius: 3px;
}
.row__dns {
display: flex;
flex-direction: row;
align-items: center;
width: auto;
justify-content: space-between;
-unity-text-align: middle-left;
}
.row__status {
display: flex;
flex-direction: row;
align-items: center;
width: auto;
justify-content: space-between;
-unity-text-align: middle-left;
}
.label__header {
-unity-font-style: bold
}
.label__status {
-unity-font-style: bold;
border-radius: 2px;
width: 100px;
color: #fff;
-unity-text-align: middle-center;
}
.label__info-text {
padding: 8px;
margin: 4px;
border-radius: 3px;
-unity-text-align: middle-center;
white-space: normal;
background-color: rgba(42, 42, 42, 0.5);
}
.container {
margin: 8px 0px;
}
.bg--secondary {
background-color: #8a8a8a;
}
.bg--success {
background-color: #90be6d;
}
.bg--danger {
background-color: #f94144;
}
.bg--warning {
background-color: #f9c74f;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: da5e3f58bd8cde14789f7c61df3f59f4
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@ -0,0 +1,242 @@
using IO.Swagger.Model;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Edgegap
{
static class EdgegapServerDataManagerUtils
{
public static Label GetHeader(string text)
{
Label header = new Label(text);
header.AddToClassList("label__header");
return header;
}
public static VisualElement GetHeaderRow()
{
VisualElement row = new VisualElement();
row.AddToClassList("row__port-table");
row.AddToClassList("label__header");
row.Add(new Label("Name"));
row.Add(new Label("External"));
row.Add(new Label("Internal"));
row.Add(new Label("ProtocolStr"));
row.Add(new Label("Link"));
return row;
}
public static VisualElement GetRowFromPortResponse(PortMapping port)
{
VisualElement row = new VisualElement();
row.AddToClassList("row__port-table");
row.AddToClassList("focusable");
row.Add(new Label(port.Name));
row.Add(new Label(port.External.ToString()));
row.Add(new Label(port.Internal.ToString()));
row.Add(new Label(port.Protocol));
row.Add(GetCopyButton("Copy", port.Link));
return row;
}
public static Button GetCopyButton(string btnText, string copiedText)
{
Button copyBtn = new Button();
copyBtn.text = btnText;
copyBtn.clickable.clicked += () => GUIUtility.systemCopyBuffer = copiedText;
return copyBtn;
}
public static Button GetLinkButton(string btnText, string targetUrl)
{
Button copyBtn = new Button();
copyBtn.text = btnText;
copyBtn.clickable.clicked += () => UnityEngine.Application.OpenURL(targetUrl);
return copyBtn;
}
public static Label GetInfoText(string innerText)
{
Label infoText = new Label(innerText);
infoText.AddToClassList("label__info-text");
return infoText;
}
}
/// <summary>
/// Utility class to centrally manage the Edgegap server data, and create / update the elements displaying the server info.
/// </summary>
public static class EdgegapServerDataManager
{
internal static Status _serverData;
private static ApiEnvironment _apiEnvironment;
// UI elements
private static readonly StyleSheet _serverDataStylesheet;
private static readonly List<VisualElement> _serverDataContainers = new List<VisualElement>();
public static Status GetServerStatus() => _serverData;
static EdgegapServerDataManager()
{
#if UNITY_EDITOR
_serverDataStylesheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Edgegap/Editor/EdgegapServerData.uss");
#endif
}
public static void RegisterServerDataContainer(VisualElement serverDataContainer)
{
_serverDataContainers.Add(serverDataContainer);
}
public static void DeregisterServerDataContainer(VisualElement serverDataContainer)
{
_serverDataContainers.Remove(serverDataContainer);
}
public static void SetServerData(Status serverData, ApiEnvironment apiEnvironment)
{
_serverData = serverData;
_apiEnvironment = apiEnvironment;
RefreshServerDataContainers();
}
private static VisualElement GetStatusSection()
{
ServerStatus serverStatus = _serverData.GetServerStatus();
string dashboardUrl = _apiEnvironment.GetDashboardUrl();
string requestId = _serverData.RequestId;
string deploymentDashboardUrl = "";
if (!string.IsNullOrEmpty(requestId) && !string.IsNullOrEmpty(dashboardUrl))
{
deploymentDashboardUrl = $"{dashboardUrl}/arbitrium/deployment/read/{requestId}/";
}
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(EdgegapServerDataManagerUtils.GetHeader("Server Status"));
VisualElement row = new VisualElement();
row.AddToClassList("row__status");
// Status pill
Label statusLabel = new Label(serverStatus.GetLabelText());
statusLabel.AddToClassList(serverStatus.GetStatusBgClass());
statusLabel.AddToClassList("label__status");
row.Add(statusLabel);
// Link to dashboard
if (!string.IsNullOrEmpty(deploymentDashboardUrl))
{
row.Add(EdgegapServerDataManagerUtils.GetLinkButton("See in the dashboard", deploymentDashboardUrl));
}
else
{
row.Add(new Label("Could not resolve link to this deployment"));
}
container.Add(row);
return container;
}
private static VisualElement GetDnsSection()
{
string serverDns = _serverData.Fqdn;
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(EdgegapServerDataManagerUtils.GetHeader("Server DNS"));
VisualElement row = new VisualElement();
row.AddToClassList("row__dns");
row.AddToClassList("focusable");
row.Add(new Label(serverDns));
row.Add(EdgegapServerDataManagerUtils.GetCopyButton("Copy", serverDns));
container.Add(row);
return container;
}
private static VisualElement GetPortsSection()
{
List<PortMapping> serverPorts = _serverData.Ports.Values.ToList();
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(EdgegapServerDataManagerUtils.GetHeader("Server PortsDict"));
container.Add(EdgegapServerDataManagerUtils.GetHeaderRow());
VisualElement portList = new VisualElement();
if (serverPorts.Count > 0)
{
foreach (PortMapping port in serverPorts)
{
portList.Add(EdgegapServerDataManagerUtils.GetRowFromPortResponse(port));
}
}
else
{
portList.Add(new Label("No port configured for this app version."));
}
container.Add(portList);
return container;
}
public static VisualElement GetServerDataVisualTree()
{
VisualElement serverDataTree = new VisualElement();
serverDataTree.styleSheets.Add(_serverDataStylesheet);
bool hasServerData = _serverData != null;
bool isReady = hasServerData && _serverData.GetServerStatus().IsOneOf(ServerStatus.Ready, ServerStatus.Error);
if (hasServerData)
{
serverDataTree.Add(GetStatusSection());
if (isReady)
{
serverDataTree.Add(GetDnsSection());
serverDataTree.Add(GetPortsSection());
}
else
{
serverDataTree.Add(EdgegapServerDataManagerUtils.GetInfoText("Additional information will be displayed when the server is ready."));
}
}
else
{
serverDataTree.Add(EdgegapServerDataManagerUtils.GetInfoText("Server data will be displayed here when a server is running."));
}
return serverDataTree;
}
private static void RefreshServerDataContainers()
{
foreach (VisualElement serverDataContainer in _serverDataContainers)
{
serverDataContainer.Clear();
serverDataContainer.Add(GetServerDataVisualTree()); // Cannot reuse a same instance of VisualElement
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39f5f27c13279a34eb116630a00e41c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using UnityEngine;
using Edgegap;
using IO.Swagger.Model;
/// <summary>
/// This script acts as an interface to display and use the necessary variables from the Edgegap tool.
/// The server info can be accessed from the tool window, as well as through the public script property.
/// </summary>
public class EdgegapToolScript : MonoBehaviour
{
public Status ServerStatus => EdgegapServerDataManager.GetServerStatus();
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5963202433da25448a22def99f5a598b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,988 @@
// MIRROR CHANGE: disable this completely. otherwise InitUIElements can still throw NRE.
/*
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Net;
using System.Text;
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using IO.Swagger.Model;
using UnityEditor.Build.Reporting;
using Application = UnityEngine.Application;
namespace Edgegap
{
public class EdgegapWindow : EditorWindow
{
// MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
// static readonly HttpClient _httpClient = new HttpClient();
// END MIRROR CHANGE
const string EditorDataSerializationName = "EdgegapSerializationData";
const int ServerStatusCronjobIntervalMs = 10000; // Interval at which the server status is updated
// MIRROR CHANGE
// get the path of this .cs file so we don't need to hardcode paths to
// the .uxml and .uss files:
// https://forum.unity.com/threads/too-many-hard-coded-paths-in-the-templates-and-documentation.728138/
// this way users can move this folder without breaking UIToolkit paths.
internal string StylesheetPath =>
Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this)));
// END MIRROR CHANGE
readonly System.Timers.Timer _updateServerStatusCronjob = new System.Timers.Timer(ServerStatusCronjobIntervalMs);
[SerializeField] string _userExternalIp;
[SerializeField] string _apiKey;
[SerializeField] ApiEnvironment _apiEnvironment;
[SerializeField] string _appName;
[SerializeField] string _appVersionName;
[SerializeField] string _deploymentRequestId;
[SerializeField] string _containerRegistry;
[SerializeField] string _containerImageRepo;
[SerializeField] string _containerImageTag;
[SerializeField] bool _autoIncrementTag = true;
VisualTreeAsset _visualTree;
bool _shouldUpdateServerStatus = false;
// Interactable elements
EnumField _apiEnvironmentSelect;
TextField _apiKeyInput;
TextField _appNameInput;
TextField _appVersionNameInput;
TextField _containerRegistryInput;
TextField _containerImageRepoInput;
TextField _containerImageTagInput;
Toggle _autoIncrementTagInput;
Button _connectionButton;
Button _serverActionButton;
Button _documentationBtn;
Button _buildAndPushServerBtn;
// Readonly elements
Label _connectionStatusLabel;
VisualElement _serverDataContainer;
// server data manager
StyleSheet _serverDataStylesheet;
List<VisualElement> _serverDataContainers = new List<VisualElement>();
[Obsolete("See EdgegapWindowV2.ShowEdgegapToolWindow()")]
// [MenuItem("Edgegap/Server Management")]
public static void ShowEdgegapToolWindow()
{
EdgegapWindow window = GetWindow<EdgegapWindow>();
window.titleContent = new GUIContent("Edgegap Hosting"); // MIRROR CHANGE
}
protected void OnEnable()
{
// Set root VisualElement and style
// BEGIN MIRROR CHANGE
_visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{StylesheetPath}/EdgegapWindow.uxml");
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"{StylesheetPath}/EdgegapWindow.uss");
_serverDataStylesheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"{StylesheetPath}/EdgegapServerData.uss");
// END MIRROR CHANGE
rootVisualElement.styleSheets.Add(styleSheet);
LoadToolData();
if (string.IsNullOrWhiteSpace(_userExternalIp))
{
_userExternalIp = GetExternalIpAddress();
}
}
protected void Update()
{
if (_shouldUpdateServerStatus)
{
_shouldUpdateServerStatus = false;
UpdateServerStatus();
}
}
public void CreateGUI()
{
rootVisualElement.Clear();
_visualTree.CloneTree(rootVisualElement);
InitUIElements();
SyncFormWithObject();
bool hasActiveDeployment = !string.IsNullOrEmpty(_deploymentRequestId);
if (hasActiveDeployment)
{
RestoreActiveDeployment();
}
else
{
DisconnectCallback();
}
}
protected void OnDestroy()
{
bool deploymentActive = !string.IsNullOrEmpty(_deploymentRequestId);
if (deploymentActive)
{
EditorUtility.DisplayDialog(
"Warning",
$"You have an active deployment ({_deploymentRequestId}) that won't be stopped automatically.",
"Ok"
);
}
}
protected void OnDisable()
{
SyncObjectWithForm();
SaveToolData();
DeregisterServerDataContainer(_serverDataContainer);
}
/// <summary>
/// Binds the form inputs to the associated variables and initializes the inputs as required.
/// Requires the VisualElements to be loaded before this call. Otherwise, the elements cannot be found.
/// </summary>
void InitUIElements()
{
_apiEnvironmentSelect = rootVisualElement.Q<EnumField>("environmentSelect");
_apiKeyInput = rootVisualElement.Q<TextField>("apiKey");
_appNameInput = rootVisualElement.Q<TextField>("appName");
_appVersionNameInput = rootVisualElement.Q<TextField>("appVersionName");
_containerRegistryInput = rootVisualElement.Q<TextField>("containerRegistry");
_containerImageRepoInput = rootVisualElement.Q<TextField>("containerImageRepo");
_containerImageTagInput = rootVisualElement.Q<TextField>("tag");
_autoIncrementTagInput = rootVisualElement.Q<Toggle>("autoIncrementTag");
_connectionButton = rootVisualElement.Q<Button>("connectionBtn");
_serverActionButton = rootVisualElement.Q<Button>("serverActionBtn");
_documentationBtn = rootVisualElement.Q<Button>("documentationBtn");
_buildAndPushServerBtn = rootVisualElement.Q<Button>("buildAndPushBtn");
_buildAndPushServerBtn.clickable.clicked += BuildAndPushServer;
_connectionStatusLabel = rootVisualElement.Q<Label>("connectionStatusLabel");
_serverDataContainer = rootVisualElement.Q<VisualElement>("serverDataContainer");
// Load initial server data UI element and register for updates.
VisualElement serverDataElement = GetServerDataVisualTree();
RegisterServerDataContainer(serverDataElement);
_serverDataContainer.Clear();
_serverDataContainer.Add(serverDataElement);
_documentationBtn.clickable.clicked += OpenDocumentationCallback;
// Init the ApiEnvironment dropdown
_apiEnvironmentSelect.Init(ApiEnvironment.Console);
}
/// <summary>
/// With a call to an external resource, determines the current user's public IP address.
/// </summary>
/// <returns>External IP address</returns>
string GetExternalIpAddress()
{
string externalIpString = new WebClient()
.DownloadString("http://icanhazip.com")
.Replace("\\r\\n", "")
.Replace("\\n", "")
.Trim();
IPAddress externalIp = IPAddress.Parse(externalIpString);
return externalIp.ToString();
}
void OpenDocumentationCallback()
{
// MIRROR CHANGE
// ApiEnvironment selectedApiEnvironment = (ApiEnvironment)_apiEnvironmentSelect.value;
// string documentationUrl = selectedApiEnvironment.GetDocumentationUrl();
//
// if (!string.IsNullOrEmpty(documentationUrl))
// {
// UnityEngine.Application.OpenURL(documentationUrl);
// }
// else
// {
// string apiEnvName = Enum.GetName(typeof(ApiEnvironment), selectedApiEnvironment);
// Debug.LogWarning($"Could not open documentation for api environment {apiEnvName}: No documentation URL.");
// }
// link to the easiest documentation
Application.OpenURL("https://mirror-networking.gitbook.io/docs/hosting/edgegap-hosting-plugin-guide");
// END MIRROR CHANGE
}
void ConnectCallback()
{
ApiEnvironment selectedApiEnvironment = (ApiEnvironment)_apiEnvironmentSelect.value;
string selectedAppName = _appNameInput.value;
string selectedVersionName = _appVersionNameInput.value;
string selectedApiKey = _apiKeyInput.value;
bool validAppName = !string.IsNullOrEmpty(selectedAppName) && !string.IsNullOrWhiteSpace(selectedAppName);
bool validVersionName = !string.IsNullOrEmpty(selectedVersionName) && !string.IsNullOrWhiteSpace(selectedVersionName);
bool validApiKey = selectedApiKey.StartsWith("token ");
if (validAppName && validVersionName && validApiKey)
{
string apiKeyValue = selectedApiKey.Substring(6);
Connect(selectedApiEnvironment, selectedAppName, selectedVersionName, apiKeyValue);
}
else
{
EditorUtility.DisplayDialog(
"Could not connect - Invalid data",
"The data provided is invalid. " +
"Make sure every field is filled, and that you provide your complete Edgegap API token " +
"(including the \"token\" part).",
"Ok"
);
}
}
// MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
HttpClient CreateHttpClient()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_apiEnvironment.GetApiUrl());
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string token = _apiKeyInput.value.Substring(6);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", token);
return httpClient;
}
// END MIRROR CHANGE
async void Connect(
ApiEnvironment selectedApiEnvironment,
string selectedAppName,
string selectedAppVersionName,
string selectedApiTokenValue
)
{
SetToolUIState(ToolState.Connecting);
// Make HTTP request
HttpClient _httpClient = CreateHttpClient(); // MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
string path = $"/v1/app/{selectedAppName}/version/{selectedAppVersionName}"; // MIRROR CHANGE: use selectedAppName and selectedAppVersionName instead of _appName & _appVersionName
HttpResponseMessage response = await _httpClient.GetAsync(path);
if (response.IsSuccessStatusCode)
{
SyncObjectWithForm();
SetToolUIState(ToolState.Connected);
}
else
{
int status = (int)response.StatusCode;
string title;
string message;
if (status == 401)
{
string apiEnvName = Enum.GetName(typeof(ApiEnvironment), selectedApiEnvironment);
title = "Invalid credentials";
message = $"Could not find an Edgegap account with this API key for the {apiEnvName} environment.";
}
else if (status == 404)
{
title = "App not found";
message = $"Could not find app {selectedAppName} with version {selectedAppVersionName}.";
}
else
{
title = "Oops";
message = $"There was an error while connecting you to the Edgegap API. Please try again later.";
}
EditorUtility.DisplayDialog(title, message, "Ok");
SetToolUIState(ToolState.Disconnected);
}
}
void DisconnectCallback()
{
if (string.IsNullOrEmpty(_deploymentRequestId))
{
SetToolUIState(ToolState.Disconnected);
}
else
{
EditorUtility.DisplayDialog("Cannot disconnect", "Make sure no server is running in the Edgegap tool before disconnecting", "Ok");
}
}
float ProgressCounter = 0;
// MIRROR CHANGE: added title parameter for more detailed progress while waiting
void ShowBuildWorkInProgress(string title, string status)
{
EditorUtility.DisplayProgressBar(title, status, ProgressCounter++ / 50);
}
// END MIRROR CHANGE
async void BuildAndPushServer()
{
SetToolUIState(ToolState.Building);
SyncObjectWithForm();
ProgressCounter = 0;
Action<string> onError = (msg) =>
{
EditorUtility.DisplayDialog("Error", msg, "Ok");
SetToolUIState(ToolState.Connected);
};
try
{
// check for installation and setup docker file
if (!await EdgegapBuildUtils.DockerSetupAndInstallationCheck())
{
onError("Docker installation not found. Docker can be downloaded from:\n\nhttps://www.docker.com/");
return;
}
// MIRROR CHANGE
// make sure Linux build target is installed before attemping to build.
// if it's not installed, tell the user about it.
if (!BuildPipeline.IsBuildTargetSupported(BuildTargetGroup.Standalone, BuildTarget.StandaloneLinux64))
{
onError($"Linux Build Support is missing.\n\nPlease open Unity Hub -> Installs -> Unity {Application.unityVersion} -> Add Modules -> Linux Build Support (IL2CPP & Mono & Dedicated Server) -> Install\n\nAfterwards restart Unity!");
return;
}
// END MIRROR CHANGE
// create server build
BuildReport buildResult = EdgegapBuildUtils.BuildServer();
if (buildResult.summary.result != BuildResult.Succeeded)
{
onError("Edgegap build failed, please check the Unity console logs.");
return;
}
string registry = _containerRegistry;
string imageName = _containerImageRepo;
string tag = _containerImageTag;
// MIRROR CHANGE ///////////////////////////////////////////////
// registry, repository and tag can not contain whitespaces.
// otherwise the docker command will throw an error:
// "ERROR: "docker buildx build" requires exactly 1 argument."
// catch this early and notify the user immediately.
if (registry.Contains(" "))
{
onError($"Container Registry is not allowed to contain whitespace: '{registry}'");
return;
}
if (imageName.Contains(" "))
{
onError($"Image Repository is not allowed to contain whitespace: '{imageName}'");
return;
}
if (tag.Contains(" "))
{
onError($"Tag is not allowed to contain whitespace: '{tag}'");
return;
}
// END MIRROR CHANGE ///////////////////////////////////////////
// increment tag for quicker iteration
if (_autoIncrementTag)
{
tag = EdgegapBuildUtils.IncrementTag(tag);
}
// create docker image
await EdgegapBuildUtils.RunCommand_DockerBuild(registry, imageName, tag, status => ShowBuildWorkInProgress("Building Docker Image", status));
SetToolUIState(ToolState.Pushing);
// push docker image
(bool result, string error) = await EdgegapBuildUtils.RunCommand_DockerPush(registry, imageName, tag, status => ShowBuildWorkInProgress("Uploading Docker Image (this may take a while)", status));
if (!result)
{
// catch common issues with detailed solutions
if (error.Contains("Cannot connect to the Docker daemon"))
{
onError($"{error}\nTo solve this, you can install and run Docker Desktop from:\n\nhttps://www.docker.com/products/docker-desktop");
return;
}
if (error.Contains("unauthorized to access repository"))
{
onError($"Docker authorization failed:\n\n{error}\nTo solve this, you can open a terminal and enter 'docker login {registry}', then enter your credentials.");
return;
}
// project not found?
if (Regex.IsMatch(error, @".*project .* not found.*", RegexOptions.IgnoreCase))
{
onError($"{error}\nTo solve this, make sure that Image Repository is 'project/game' where 'project' is from the Container Registry page on the Edgegap website.");
return;
}
// otherwise show generic error message
onError($"Unable to push docker image to registry. Please make sure you're logged in to {registry} and check the following error:\n\n{error}");
return;
}
// update edgegap server settings for new tag
ShowBuildWorkInProgress("Build and Push", "Updating server info on Edgegap");
await UpdateAppTagOnEdgegap(tag);
// cleanup
_containerImageTag = tag;
SyncFormWithObject();
SetToolUIState(ToolState.Connected);
Debug.Log("Server built and pushed successfully");
}
catch (Exception ex)
{
Debug.LogError(ex);
onError($"Edgegap build and push failed with Error: {ex}");
}
finally
{
// MIRROR CHANGE: always clear otherwise it gets stuck there forever!
EditorUtility.ClearProgressBar();
}
}
async Task UpdateAppTagOnEdgegap(string newTag)
{
string path = $"/v1/app/{_appName}/version/{_appVersionName}";
// Setup post data
AppVersionUpdatePatchData updatePatchData = new AppVersionUpdatePatchData { DockerImage = _containerImageRepo, DockerRegistry = _containerRegistry, DockerTag = newTag };
string json = JsonConvert.SerializeObject(updatePatchData);
StringContent patchData = new StringContent(json, Encoding.UTF8, "application/json");
// Make HTTP request
HttpClient _httpClient = CreateHttpClient(); // MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), path);
request.Content = patchData;
HttpResponseMessage response = await _httpClient.SendAsync(request);
string content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Could not update Edgegap server tag. Got {(int)response.StatusCode} with response:\n{content}");
}
}
async void StartServerCallback()
{
SetToolUIState(ToolState.ProcessingDeployment); // Prevents being called multiple times.
const string path = "/v1/deploy";
// Setup post data
DeployPostData deployPostData = new DeployPostData(_appName, _appVersionName, new List<string> { _userExternalIp });
string json = JsonConvert.SerializeObject(deployPostData);
StringContent postData = new StringContent(json, Encoding.UTF8, "application/json");
// Make HTTP request
HttpClient _httpClient = CreateHttpClient(); // MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
HttpResponseMessage response = await _httpClient.PostAsync(path, postData);
string content = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
// Parse response
Deployment parsedResponse = JsonConvert.DeserializeObject<Deployment>(content);
_deploymentRequestId = parsedResponse.RequestId;
UpdateServerStatus();
StartServerStatusCronjob();
}
else
{
Debug.LogError($"Could not start Edgegap server. Got {(int)response.StatusCode} with response:\n{content}");
SetToolUIState(ToolState.Connected);
}
}
async void StopServerCallback()
{
string path = $"/v1/stop/{_deploymentRequestId}";
// Make HTTP request
HttpClient _httpClient = CreateHttpClient(); // MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
HttpResponseMessage response = await _httpClient.DeleteAsync(path);
if (response.IsSuccessStatusCode)
{
UpdateServerStatus();
SetToolUIState(ToolState.ProcessingDeployment);
}
else
{
// Parse response
string content = await response.Content.ReadAsStringAsync();
Debug.LogError($"Could not stop Edgegap server. Got {(int)response.StatusCode} with response:\n{content}");
}
}
void StartServerStatusCronjob()
{
_updateServerStatusCronjob.Elapsed += (sourceObject, elaspedEvent) => _shouldUpdateServerStatus = true;
_updateServerStatusCronjob.AutoReset = true;
_updateServerStatusCronjob.Start();
}
void StopServerStatusCronjob() => _updateServerStatusCronjob.Stop();
async void UpdateServerStatus()
{
Status serverStatusResponse = await FetchServerStatus();
ToolState toolState;
ServerStatus serverStatus = serverStatusResponse.GetServerStatus();
if (serverStatus == ServerStatus.Terminated)
{
SetServerData(null, _apiEnvironment);
if (_updateServerStatusCronjob.Enabled)
{
StopServerStatusCronjob();
}
_deploymentRequestId = null;
toolState = ToolState.Connected;
}
else
{
SetServerData(serverStatusResponse, _apiEnvironment);
if (serverStatus == ServerStatus.Ready || serverStatus == ServerStatus.Error)
{
toolState = ToolState.DeploymentRunning;
}
else
{
toolState = ToolState.ProcessingDeployment;
}
}
SetToolUIState(toolState);
}
async Task<Status> FetchServerStatus()
{
string path = $"/v1/status/{_deploymentRequestId}";
// Make HTTP request
HttpClient _httpClient = CreateHttpClient(); // MIRROR CHANGE: create HTTPClient in-place to avoid InvalidOperationExceptions when reusing
HttpResponseMessage response = await _httpClient.GetAsync(path);
// Parse response
string content = await response.Content.ReadAsStringAsync();
Status parsedData;
if (response.IsSuccessStatusCode)
{
parsedData = JsonConvert.DeserializeObject<Status>(content);
}
else
{
if ((int)response.StatusCode == 400)
{
Debug.LogError("The deployment that was active in the tool is now unreachable. Considering it Terminated.");
parsedData = new Status() { CurrentStatus = ServerStatus.Terminated.GetLabelText() };
}
else
{
Debug.LogError(
$"Could not fetch status of Edgegap deployment {_deploymentRequestId}. " +
$"Got {(int)response.StatusCode} with response:\n{content}"
);
parsedData = new Status() { CurrentStatus = ServerStatus.NA.GetLabelText() };
}
}
return parsedData;
}
void RestoreActiveDeployment()
{
ConnectCallback();
_shouldUpdateServerStatus = true;
StartServerStatusCronjob();
}
void SyncObjectWithForm()
{
if (_apiKeyInput == null) return; // MIRROR CHANGE: fix NRE when this is called before UI elements were assgned
_apiKey = _apiKeyInput.value;
_apiEnvironment = (ApiEnvironment)_apiEnvironmentSelect.value;
_appName = _appNameInput.value;
_appVersionName = _appVersionNameInput.value;
// MIRROR CHANGE ///////////////////////////////////////////////////
// registry, repository and tag can not contain whitespaces.
// otherwise it'll throw an error:
// "ERROR: "docker buildx build" requires exactly 1 argument."
// trim whitespace in case users accidentally added some.
_containerRegistry = _containerRegistryInput.value.Trim();
_containerImageTag = _containerImageTagInput.value.Trim();
_containerImageRepo = _containerImageRepoInput.value.Trim();
// END MIRROR CHANGE ///////////////////////////////////////////////
_autoIncrementTag = _autoIncrementTagInput.value;
}
void SyncFormWithObject()
{
_apiKeyInput.value = _apiKey;
_apiEnvironmentSelect.value = _apiEnvironment;
_appNameInput.value = _appName;
_appVersionNameInput.value = _appVersionName;
_containerRegistryInput.value = _containerRegistry;
_containerImageTagInput.value = _containerImageTag;
_containerImageRepoInput.value = _containerImageRepo;
_autoIncrementTagInput.value = _autoIncrementTag;
}
void SetToolUIState(ToolState toolState)
{
SetConnectionInfoUI(toolState);
SetConnectionButtonUI(toolState);
SetServerActionUI(toolState);
SetDockerRepoInfoUI(toolState);
}
void SetDockerRepoInfoUI(ToolState toolState)
{
bool connected = toolState.CanStartDeployment();
_containerRegistryInput.SetEnabled(connected);
_autoIncrementTagInput.SetEnabled(connected);
_containerImageRepoInput.SetEnabled(connected);
_containerImageTagInput.SetEnabled(connected);
}
void SetConnectionInfoUI(ToolState toolState)
{
bool canEditConnectionInfo = toolState.CanEditConnectionInfo();
_apiKeyInput.SetEnabled(canEditConnectionInfo);
_apiEnvironmentSelect.SetEnabled(canEditConnectionInfo);
_appNameInput.SetEnabled(canEditConnectionInfo);
_appVersionNameInput.SetEnabled(canEditConnectionInfo);
}
void SetConnectionButtonUI(ToolState toolState)
{
bool canConnect = toolState.CanConnect();
bool canDisconnect = toolState.CanDisconnect();
_connectionButton.SetEnabled(canConnect || canDisconnect);
// A bit dirty, but ensures the callback is not bound multiple times on the button.
_connectionButton.clickable.clicked -= ConnectCallback;
_connectionButton.clickable.clicked -= DisconnectCallback;
if (canConnect || toolState == ToolState.Connecting)
{
_connectionButton.text = "Connect";
_connectionStatusLabel.text = "Awaiting connection";
_connectionStatusLabel.RemoveFromClassList("text--success");
_connectionButton.clickable.clicked += ConnectCallback;
}
else
{
_connectionButton.text = "Disconnect";
_connectionStatusLabel.text = "Connected";
_connectionStatusLabel.AddToClassList("text--success");
_connectionButton.clickable.clicked += DisconnectCallback;
}
}
void SetServerActionUI(ToolState toolState)
{
bool canStartDeployment = toolState.CanStartDeployment();
bool canStopDeployment = toolState.CanStopDeployment();
// A bit dirty, but ensures the callback is not bound multiple times on the button.
_serverActionButton.clickable.clicked -= StartServerCallback;
_serverActionButton.clickable.clicked -= StopServerCallback;
_serverActionButton.SetEnabled(canStartDeployment || canStopDeployment);
_buildAndPushServerBtn.SetEnabled(canStartDeployment);
if (canStopDeployment)
{
_serverActionButton.text = "Stop Server";
_serverActionButton.clickable.clicked += StopServerCallback;
}
else
{
_serverActionButton.text = "Start Server";
_serverActionButton.clickable.clicked += StartServerCallback;
}
}
// server data manager /////////////////////////////////////////////////
public void RegisterServerDataContainer(VisualElement serverDataContainer)
{
_serverDataContainers.Add(serverDataContainer);
}
public void DeregisterServerDataContainer(VisualElement serverDataContainer)
{
_serverDataContainers.Remove(serverDataContainer);
}
public void SetServerData(Status serverData, ApiEnvironment apiEnvironment)
{
EdgegapServerDataManager._serverData = serverData;
RefreshServerDataContainers();
}
public Label GetHeader(string text)
{
Label header = new Label(text);
header.AddToClassList("label__header");
return header;
}
public VisualElement GetHeaderRow()
{
VisualElement row = new VisualElement();
row.AddToClassList("row__port-table");
row.AddToClassList("label__header");
row.Add(new Label("Name"));
row.Add(new Label("External"));
row.Add(new Label("Internal"));
row.Add(new Label("Protocol"));
row.Add(new Label("Link"));
return row;
}
public VisualElement GetRowFromPortResponse(PortMapping port)
{
VisualElement row = new VisualElement();
row.AddToClassList("row__port-table");
row.AddToClassList("focusable");
row.Add(new Label(port.Name));
row.Add(new Label(port.External.ToString()));
row.Add(new Label(port.Internal.ToString()));
row.Add(new Label(port.Protocol));
row.Add(GetCopyButton("Copy", port.Link));
return row;
}
public Button GetCopyButton(string btnText, string copiedText)
{
Button copyBtn = new Button();
copyBtn.text = btnText;
copyBtn.clickable.clicked += () => GUIUtility.systemCopyBuffer = copiedText;
return copyBtn;
}
public Button GetLinkButton(string btnText, string targetUrl)
{
Button copyBtn = new Button();
copyBtn.text = btnText;
copyBtn.clickable.clicked += () => UnityEngine.Application.OpenURL(targetUrl);
return copyBtn;
}
public Label GetInfoText(string innerText)
{
Label infoText = new Label(innerText);
infoText.AddToClassList("label__info-text");
return infoText;
}
VisualElement GetStatusSection()
{
ServerStatus serverStatus = EdgegapServerDataManager._serverData.GetServerStatus();
string dashboardUrl = _apiEnvironment.GetDashboardUrl();
string requestId = EdgegapServerDataManager._serverData.RequestId;
string deploymentDashboardUrl = "";
if (!string.IsNullOrEmpty(requestId) && !string.IsNullOrEmpty(dashboardUrl))
{
deploymentDashboardUrl = $"{dashboardUrl}/arbitrium/deployment/read/{requestId}/";
}
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(GetHeader("Server Status"));
VisualElement row = new VisualElement();
row.AddToClassList("row__status");
// Status pill
Label statusLabel = new Label(serverStatus.GetLabelText());
statusLabel.AddToClassList(serverStatus.GetStatusBgClass());
statusLabel.AddToClassList("label__status");
row.Add(statusLabel);
// Link to dashboard
if (!string.IsNullOrEmpty(deploymentDashboardUrl))
{
row.Add(GetLinkButton("See in the dashboard", deploymentDashboardUrl));
}
else
{
row.Add(new Label("Could not resolve link to this deployment"));
}
container.Add(row);
return container;
}
VisualElement GetDnsSection()
{
string serverDns = EdgegapServerDataManager._serverData.Fqdn;
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(GetHeader("Server DNS"));
VisualElement row = new VisualElement();
row.AddToClassList("row__dns");
row.AddToClassList("focusable");
row.Add(new Label(serverDns));
row.Add(GetCopyButton("Copy", serverDns));
container.Add(row);
return container;
}
VisualElement GetPortsSection()
{
List<PortMapping> serverPorts = EdgegapServerDataManager._serverData.Ports.Values.ToList();
VisualElement container = new VisualElement();
container.AddToClassList("container");
container.Add(GetHeader("Server Ports"));
container.Add(GetHeaderRow());
VisualElement portList = new VisualElement();
if (serverPorts.Count > 0)
{
foreach (PortMapping port in serverPorts)
{
portList.Add(GetRowFromPortResponse(port));
}
}
else
{
portList.Add(new Label("No port configured for this app version."));
}
container.Add(portList);
return container;
}
public VisualElement GetServerDataVisualTree()
{
VisualElement serverDataTree = new VisualElement();
serverDataTree.styleSheets.Add(_serverDataStylesheet);
bool hasServerData = EdgegapServerDataManager._serverData != null;
bool isReady = hasServerData && EdgegapServerDataManager. _serverData.GetServerStatus().IsOneOf(ServerStatus.Ready, ServerStatus.Error);
if (hasServerData)
{
serverDataTree.Add(GetStatusSection());
if (isReady)
{
serverDataTree.Add(GetDnsSection());
serverDataTree.Add(GetPortsSection());
}
else
{
serverDataTree.Add(GetInfoText("Additional information will be displayed when the server is ready."));
}
}
else
{
serverDataTree.Add(GetInfoText("Server data will be displayed here when a server is running."));
}
return serverDataTree;
}
void RefreshServerDataContainers()
{
foreach (VisualElement serverDataContainer in _serverDataContainers)
{
serverDataContainer.Clear();
serverDataContainer.Add(GetServerDataVisualTree()); // Cannot reuse a same instance of VisualElement
}
}
// save & load /////////////////////////////////////////////////////////
/// <summary>
/// Save the tool's serializable data to the EditorPrefs to allow persistence across restarts.
/// Any field with [SerializeField] will be saved.
/// </summary>
void SaveToolData()
{
string data = JsonUtility.ToJson(this, false);
EditorPrefs.SetString(EditorDataSerializationName, data);
}
/// <summary>
/// Load the tool's serializable data from the EditorPrefs to the object, restoring the tool's state.
/// </summary>
void LoadToolData()
{
string data = EditorPrefs.GetString(EditorDataSerializationName, JsonUtility.ToJson(this, false));
JsonUtility.FromJsonOverwrite(data, this);
}
}
}
#endif
*/

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28bfb13cf2845d04eb5cbee51f9353d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,215 @@
#serverDataContainer {
margin: 4px 0 0 0;
}
.content {
position: relative;
height: auto;
overflow: hidden;
padding: 8px 0 8px 0;
}
.background {
position: absolute;
height: 400px;
width: 400px;
top: -10px;
right: -150px;
/* MIRROR CHANGE: disable hardcoded image path. this is inserted from the script now.
background-image: url('project://database/Assets/Edgegap/Editor/Images/logo_transparent_400_alpha25.png?fileID=21300000&guid=b7012da4ebf9008458abc3ef9a741f3c&type=3#logo_transparent_400_alpha25');
*/
-unity-background-scale-mode: scale-and-crop;
}
.text__title {
font-size: 20px;
color: rgb(36, 194, 237);
padding-top: 8px;
padding-bottom: 8px;
-unity-text-align: middle-center;
/* MIRROR CHANGE: disable hardcoded font path
-unity-font: url('project://database/Assets/Edgegap/Editor/Fonts/Src/BaronNeue.otf?fileID=12800000&guid=fb67205c672fbb04d829783b9f771fc9&type=3#BaronNeue');
*/
}
.text--muted {
color: rgb(192, 192, 192);
}
.text--success {
color: rgb(144, 190, 109);
}
.container {
padding: 4px 4px;
}
.flex {
display: flex;
flex-direction: row;
align-items: center;
width: auto;
}
.flex--wrap {
flex-wrap: wrap;
}
.flex--right {
justify-content: flex-end;
}
.flex--between {
justify-content: space-between;
}
#unity-text-input {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
height: 27px;
background-color: rgb(26, 26, 26);
}
.button-edgegap {
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-left-color: rgba(0, 0, 0, 0.35);
border-right-color: rgba(0, 0, 0, 0.35);
border-top-color: rgba(0, 0, 0, 0.35);
border-bottom-color: rgba(0, 0, 0, 0.35);
height: 27px;
-unity-font-style: bold;
background-color: rgb(36, 76, 87);
min-width: 170px;
max-width: 200px;
}
.button-edgegap:hover {
background-color: rgb(56, 96, 107);
}
.button-purple-hover:hover {
background-color: rgb(44, 30, 210);
}
.container-row-parent {
background-color: rgb(49, 49, 49);
border-top-color: rgb(0, 0, 0);
border-bottom-color: rgb(0, 0, 0);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
padding-top: 10px;
padding-bottom: 10px;
margin-bottom: 10px;
}
.text-edgegap {
font-size: 11px;
color: rgb(222, 222, 222);
/* MIRROR CHANGE: disable hardcoded font path
-unity-font-definition: url('project://database/Assets/Edgegap/Editor/Fonts/Spartan-Regular%20SDF.asset?fileID=11400000&guid=8b0fb2c68be09174f8ea5057b27a545c&type=2#Spartan-Regular SDF');
*/
}
.text-btnTxt {
font-size: 12px;
color: rgb(255, 255, 255);
-unity-font-style: bold;
}
Toggle > #unity-checkmark {
}
.unity-foldout > #unity-checkmark {
}
.bg-purple {
background-color: rgb(44, 30, 210);
}
.bg-purple:hover {
background-color: rgb(64, 50, 230);
}
.unity-foldout {
padding-top: 10px;
padding-bottom: 10px;
}
.unity-text-field {
min-width: auto;
width: 400px;
padding-right: 5px;
white-space: normal;
-unity-text-align: middle-left;
opacity: 1;
padding-bottom: 5px;
padding-top: 5px;
align-items: center;
}
.unity-text-field > Label {
}
.container-row {
background-color: rgb(37, 37, 37);
padding-top: 0;
padding-bottom: 0;
margin-bottom: 3px;
}
.checkmark-edgegap {
font-size: 12px;
}
.checkmark-edgegap #unity-checkmark {
margin-right: 15px;
}
.unity-text-field__input > TextElement {
color: rgb(255, 255, 255);
/* MIRROR CHANGE: disable hardcoded font path
-unity-font-definition: url('project://database/Assets/Edgegap/Editor/Fonts/UbuntuMono-R%20SDF.asset?fileID=11400000&guid=2635d61c9807d6c46bcb00a3d8645b37&type=2#UbuntuMono-R SDF');
*/
font-size: 12px;
white-space: nowrap;
text-overflow: clip;
}
.shakeStart {
left: 5px;
transition: left 50ms ease-in-out;
}
.shakeEnd {
left: -5px;
transition: left 50ms ease-in-out;
}
.customContainerChildTxt {
padding-left: 32px;
}
#DeploymentConnectionUrlReadOnlyTxt > #unity-text-input {
display: flex;
visibility: visible;
background-color: rgba(0, 0, 0, 0.13);
}
.button-purple-hover {
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1a2e4572c5de8840ac8d98377d409ae
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@ -0,0 +1,102 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" editor-extension-mode="True">
<Style src="project://database/Assets/Edgegap/Editor/EdgegapWindow.uss?fileID=7433441132597879392&amp;guid=b1a2e4572c5de8840ac8d98377d409ae&amp;type=3#EdgegapWindow" />
<ui:VisualElement class="content" style="padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 0;">
<ui:VisualElement name="HeaderHorizGroup" style="flex-grow: 1; flex-direction: row; height: 100px; background-color: rgb(37, 37, 37); left: 0;">
<ui:VisualElement name="header-logo-img" style="height: 67px; width: 100px; flex-direction: row; -unity-background-scale-mode: scale-to-fit; -unity-slice-left: 0; -unity-slice-top: 0; -unity-slice-right: 0; -unity-slice-bottom: 0; -unity-background-image-tint-color: rgb(255, 255, 255); -unity-slice-scale: 1px; align-self: center;" />
<ui:Label text="EDGEGAP" name="header-logo-txt" class="text__title" style="flex-direction: row; flex-grow: 1; color: rgb(255, 255, 255); font-size: 30px; -unity-text-align: middle-left; -unity-font-style: normal; -unity-font: initial; margin-left: 0; align-items: center; height: 74px;">
<ui:Button text="DEBUG" parse-escape-sequences="true" display-tooltip-when-elided="true" name="DebugBtn" tooltip="Hide me @ EdgegapWindowMetadata.SHOW_DEBUG_BTN" style="-unity-text-align: middle-left; white-space: normal; text-overflow: clip; justify-content: flex-start; align-self: flex-end; position: absolute; right: 0; top: 0; padding-top: 3px;" />
</ui:Label>
</ui:VisualElement>
</ui:VisualElement>
<ui:ScrollView name="BodyScrollView" style="background-color: rgb(37, 37, 37); height: 1132px; justify-content: flex-end;">
<ui:GroupBox name="ApiTokenHorizGroupBox" class="container-row-parent" style="flex-direction: row; margin-left: 0; margin-right: 0; margin-bottom: 10px; align-items: center; justify-content: flex-start; align-self: auto; padding-bottom: 0; padding-top: 0;">
<ui:TextField name="ApiTokenMaskedTxt" label="&lt;b&gt;API Token&lt;/b&gt;" tooltip="No token? Click the &quot;Get a Token&quot; button &gt;&gt; Click &quot;Verify&quot; after entered to unlock other features" password="true" class="text-edgegap" style="padding-left: 25px; flex-grow: 0.17;" />
<ui:Button name="ApiTokenVerifyPurpleBtn" text="Verify" tooltip="On successful validation, the remaining UI will be unlocked" class="button-edgegap text-edgegap bg-purple" style="min-width: 75px; -unity-font-style: bold; visibility: visible; display: flex; overflow: hidden;" />
<ui:Button name="ApiTokenGetBtn" text="Get a Token" class="button-edgegap text-edgegap" style="-unity-font-style: bold; width: 125px; max-width: none; min-width: auto;" />
</ui:GroupBox>
<ui:VisualElement name="PostAuthContainer" usage-hints="MaskContainer" style="flex-grow: 1; transition-timing-function: ease-in; transition-duration: 0.2s;">
<ui:Foldout text="&lt;b&gt;Application Info&lt;/b&gt;" name="ApplicationInfoFoldout" class="text-edgegap container-row-parent" style="margin-left: 0; -unity-font-style: normal;">
<ui:VisualElement name="ApplicationNameRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ApplicationNameTxt" label="Application Name" tooltip="Arbitrary name to call your app" password="false" view-data-key="ApplicationNameTxt" class="text-edgegap" />
<ui:Button name="AppLoadExistingBtn" text="Load Existing App" tooltip="Already have an existing application by this name?" class="button-edgegap text-edgegap bg-purple" style="min-width: 75px; -unity-font-style: bold; visibility: visible; display: flex; overflow: hidden;" />
</ui:VisualElement>
<ui:VisualElement name="ApplicationIconRow" class="container-row" style="flex-grow: 1;">
<ui:VisualElement name="ApplicationIconHorizGroup" class="text-edgegap" style="flex-grow: 1; flex-direction: row; align-items: center; justify-content: flex-start; align-self: stretch; width: 600px;">
<uie:ObjectField label="Application Icon" type="UnityEngine.Sprite, UnityEngine.CoreModule" name="ApplicationIconSprite" tooltip="Choose a Sprite image to use as an app icon" view-data-key="ApplicationIconSprite" style="align-items: center; justify-content: flex-start; align-self: center; width: 409px;" />
<ui:VisualElement name="SelectFileVertGroup" style="flex-grow: 0.7;">
<ui:VisualElement name="SelectFileHorizGroup" style="flex-grow: 1; flex-direction: row; align-items: center;">
<ui:Label tabindex="-1" text="Limit 200kb" parse-escape-sequences="true" display-tooltip-when-elided="true" name="SelectFileSizeLimitTooltipLabel" class="text-edgegap" style="font-size: 11px; color: rgb(147, 147, 147); -unity-text-align: upper-left;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement name="ApplicationCreateHorizRow" style="flex-grow: 1; flex-direction: row;">
<ui:Button name="ApplicationCreateBtn" text="Create Application" class="button-edgegap text-edgegap" style="flex-direction: row; -unity-text-align: middle-center; margin-left: 0;" />
<ui:Label tabindex="-1" text="{ApplicationCreateResultLabel}" parse-escape-sequences="true" display-tooltip-when-elided="true" name="ApplicationCreateResultLabel" class="text-edgegap" style="color: rgb(138, 238, 140); align-items: stretch; justify-content: flex-start; align-self: center;" />
</ui:VisualElement>
</ui:Foldout>
<ui:VisualElement name="ContainerRegistryFoldoutHorizRow" class="container-row-parent" style="flex-grow: 1; flex-direction: row;">
<ui:Foldout text="&lt;b&gt;Container Registry&lt;/b&gt;" name="ContainerRegistryFoldout" class="text-edgegap" style="flex-grow: 1; padding-bottom: 0; padding-top: 0; padding-right: 0; padding-left: 0;">
<ui:VisualElement name="ContainerPortRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerRegistryPortNumTxt" label="Port" tooltip="1024~49151 (Default `7770`)" value="7770" view-data-key="ContainerRegistryPortNumTxt" keyboard-type="NumberPad" class="text-edgegap" style="width: 250px;" />
<ui:VisualElement name="MIRROR_CHANGE_PORT_HARDCODED" class="MIRROR_CHANGE_PORT_HARDCODED" />
</ui:VisualElement>
<ui:VisualElement name="ContainerNewVersionTagRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerNewVersionTagTxt" label="New Version Tag" tooltip="eg: &quot;latest&quot; (default), &quot;v1.0.0&quot;, &quot;1.0.0&quot;" value="latest" view-data-key="ContainerNewVersionTagTxt" class="text-edgegap" />
</ui:VisualElement>
<ui:VisualElement name="ContainerUseCustomRegistryRow" view-data-key="ContainerUseCustomRegistryRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto; padding-top: 5px; padding-bottom: 5px;">
<ui:Toggle name="ContainerUseCustomRegistryToggle" label="Use Custom Container Registry" class="text-edgegap checkmark-edgegap" style="justify-content: flex-start; align-self: center; flex-direction: row-reverse; align-items: center; padding-left: 3px;" />
</ui:VisualElement>
<ui:VisualElement name="ContainerCustomRegistryWrapper" style="flex-grow: 1;">
<ui:VisualElement name="ContainerRegistryUrlRow" class="container-row customContainerRegistryMember" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerRegistryUrlTxt" label="Registry URL" tooltip="eg: `registry.edgegap.com`, `docker.io`, `harbor.edgegap.net`" class="text-edgegap customContainerChildTxt" />
</ui:VisualElement>
<ui:VisualElement name="ContainerRepositoryRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerImageRepositoryTxt" label="Repository" tooltip="eg: `edgegap-public/tutorial`, `mycompany-someid/mylowercaseapp-`" class="text-edgegap customContainerChildTxt" />
</ui:VisualElement>
<ui:VisualElement name="ContainerUsernameRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerUsernameTxt" label="Username" tooltip="eg: `robot$mycompany-someid+client-push`" class="text-edgegap customContainerChildTxt" />
</ui:VisualElement>
<ui:VisualElement name="ContainerTokenRow" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;">
<ui:TextField name="ContainerTokenTxt" label="Token" tooltip="Registry pasword/token/secret (different from top-level API Token)" password="true" class="text-edgegap customContainerChildTxt" />
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement name="ContainerBuildAndPushHorizRow" style="flex-grow: 1; flex-direction: row;">
<ui:Button name="ContainerBuildAndPushBtn" text="Build and Push" class="button-edgegap text-edgegap" style="margin-left: 0;" />
<ui:Label tabindex="-1" text="{ContainerBuildAndPushResultLabel}" parse-escape-sequences="true" display-tooltip-when-elided="true" name="ContainerBuildAndPushResultLabel" class="text-edgegap" style="color: rgb(138, 238, 140); align-items: stretch; justify-content: flex-start; align-self: center; display: none;" />
</ui:VisualElement>
</ui:Foldout>
</ui:VisualElement>
<ui:Foldout text="&lt;b&gt;Deployments&lt;/b&gt;" name="DeploymentsFoldout" class="text-edgegap container-row-parent">
<ui:VisualElement name="Row" class="container-row" style="flex-grow: 1; align-items: center; flex-direction: row; justify-content: flex-start; align-self: auto;" />
<ui:GroupBox name="DeploymentsHorizGroupBox" style="flex-direction: row; justify-content: flex-start; align-items: stretch; align-self: flex-start; margin-left: 0; padding-left: 0;">
<ui:Button name="DeploymentsRefreshBtn" text="Refresh" class="button-edgegap text-edgegap" style="flex-grow: 0.5;" />
<ui:Button name="DeploymentsCreateBtn" text="Create New Deployment" class="button-edgegap text-edgegap" style="flex-grow: 0.5; max-width: 250px; min-width: 200px;" />
</ui:GroupBox>
<ui:Label tabindex="-1" text="{DeploymentsStatusLabel}" parse-escape-sequences="true" display-tooltip-when-elided="true" name="DeploymentsStatusLabel" class="text-edgegap" style="color: rgb(138, 238, 140); white-space: normal; text-overflow: ellipsis; width: 650px; display: none; margin-left: 3px;" />
<ui:GroupBox name="DeploymentsHeadersGroupBox" class="container-row-parent" style="flex-direction: row; padding-top: 6px; padding-right: 0; padding-bottom: 3px; padding-left: 0; margin-bottom: 0; align-self: auto; justify-content: flex-start; align-items: center;">
<ui:Label name="DeploymentsConnectionStatusHeaderLabel" text="Status" style="flex-grow: 0.5; -unity-text-align: upper-left; -unity-font-style: bold; width: 75px; flex-direction: column;" />
<ui:Label name="DeploymentsConnectionURLHeaderLabel" text="URL" style="flex-grow: 1; -unity-font-style: bold; align-items: auto; justify-content: flex-start; align-self: auto;" />
<ui:Label name="DeploymentsConnectionControlHeaderLabel" text="Control" style="flex-grow: 0.08; -unity-text-align: upper-left; -unity-font-style: bold; padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 0;" />
</ui:GroupBox>
<ui:GroupBox name="DeploymentsConnectionGroupBox" style="flex-direction: row; padding-left: 0; align-items: center; align-self: stretch; justify-content: flex-start; height: 64px;">
<ui:Label name="DeploymentsConnectionStatusLabel" text="Unknown" class="text--muted" style="flex-grow: 1; flex-direction: column; -unity-text-align: middle-left; justify-content: flex-start; align-items: center; width: 86px; margin-right: 15px;" />
<ui:VisualElement name="DeploymentConnectionUrlHorizGroup" style="flex-grow: 1; flex-direction: row; width: 304px; margin-left: 25px; margin-right: 25px; padding-right: 25px; padding-left: 25px;">
<ui:TextField picking-mode="Ignore" name="DeploymentConnectionUrlReadOnlyTxt" readonly="true" tooltip="Selectable" style="padding-left: 0; -unity-text-align: middle-center; width: 297px; align-items: center;">
<ui:Button name="DeploymentConnectionCopyUrlBtn" tooltip="Copy" class="text-edgegap bg-purple" style="min-width: 15px; visibility: visible; display: flex; overflow: hidden; width: 35px; -unity-background-scale-mode: scale-to-fit; min-height: 25px; background-color: rgb(44, 30, 210); -unity-background-image-tint-color: rgb(224, 224, 224); -unity-slice-left: 1; -unity-slice-top: 1; -unity-slice-right: 1; -unity-slice-bottom: 1; translate: -40px 0;" />
</ui:TextField>
</ui:VisualElement>
<ui:VisualElement name="DeploymentsConnectionServerStopHorizBtnHorizGroup" style="flex-grow: 1; flex-direction: column; align-items: center; justify-content: center; align-self: auto;">
<ui:Button name="DeploymentsConnectionServerStopBtn" text="Stop Server" class="button-edgegap text-edgegap" style="min-width: auto; width: 125px; max-width: none; visibility: visible; display: flex;" />
<ui:Button name="DeploymentsConnectionContainerLogsBtn" text="Container Logs" class="button-edgegap text-edgegap" style="min-width: auto; width: 125px; max-width: none; visibility: visible; display: flex;" />
</ui:VisualElement>
</ui:GroupBox>
</ui:Foldout>
<ui:VisualElement name="ServerDataContainer" />
</ui:VisualElement>
<ui:GroupBox name="FooterHorizGroup" class="container-row-parent" style="flex-direction: row; justify-content: center; align-items: stretch; align-self: auto; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-left-radius: 1px; border-top-right-radius: 1px; border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; border-right-color: rgb(0, 0, 0); border-left-color: rgb(0, 0, 0); padding-right: 135px; padding-left: 150px; padding-top: 15px; padding-bottom: 15px;">
<ui:Button text="Documentation" tooltip="Opens the documentation website. The website version changes depending on the selected API environment." name="FooterDocumentationBtn" class="button-edgegap text-edgegap" style="align-items: stretch;" />
<ui:Button text="Add More Game Servers!" tooltip="Opens the documentation website. The website version changes depending on the selected API environment." name="FooterNeedMoreGameServersBtn" class="button-edgegap text-edgegap bg-purple" style="width: 220px; max-width: none; min-width: auto;" />
</ui:GroupBox>
</ui:ScrollView>
</ui:UXML>

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8f6d8a95cd8228e468ad40dbec5ca478
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@ -0,0 +1,180 @@
using System;
using Edgegap.Editor.Api.Models;
namespace Edgegap.Editor
{
/// <summary>
/// Contains static metadata / options for the EdgegapWindowV2 UI.
/// - Notable:
/// * SHOW_DEBUG_BTN
/// * LOG_LEVEL
/// * DEFAULT_VERSION_TAG
/// * SKIP_SERVER_BUILD_WHEN_PUSHING
/// * SKIP_DOCKER_IMAGE_BUILD_WHEN_PUSHING
/// </summary>
public static class EdgegapWindowMetadata
{
#region Debug
/// <summary>Log Debug+, or Errors only?</summary>
public enum LogLevel
{
Debug,
Error,
}
/// <summary>
/// Set to Debug to show more logs. Default `Error`.
/// - Error level includes "potentially-intentional" (!fatal) errors logged with Debug.Log
/// - TODO: Move opt to UI?
/// </summary>
public const LogLevel LOG_LEVEL = LogLevel.Error;
/// <summary>
/// Set to show a debug button at the top-right for arbitrary testing.
/// Default enables groups. Default `false`.
/// </summary>
public const bool SHOW_DEBUG_BTN = false;
/// <summary>
/// When running a Docker-based "Build & Push" flow, skip building the Unity server binary
/// (great for testing push flow). Default false.
/// </summary>
public static readonly bool SKIP_SERVER_BUILD_WHEN_PUSHING = false; // MIRROR CHANGE: 'const' changed to 'static readonly' to avoid 'unreachable code detected' warning
/// <summary>
/// When running a Docker-based "Build & Push" flow, skip building the Docker image
/// (great for testing registry login mechanics). Default false.
/// </summary>
public static readonly bool SKIP_DOCKER_IMAGE_BUILD_WHEN_PUSHING = false; // MIRROR CHANGE: 'const' changed to 'static readonly' to avoid 'unreachable code detected' warning
#endregion // Debug
/// <summary>Interval at which the server status is updated</summary>
public const int SERVER_STATUS_CRON_JOB_INTERVAL_MS = 10000;
public const int PORT_DEFAULT = 7770;
public const int PORT_MIN = 1024;
public const int PORT_MAX = 49151;
public const int DEPLOYMENT_AWAIT_READY_STATUS_TIMEOUT_MINS = 1;
public const int DEPLOYMENT_READY_STATUS_POLL_SECONDS = 2;
public const int DEPLOYMENT_STOP_STATUS_POLL_SECONDS = 2;
public const ProtocolType DEFAULT_PROTOCOL_TYPE = ProtocolType.UDP;
public const string READY_STATUS = "Status.READY";
public const string EDGEGAP_GET_A_TOKEN_URL = "https://app.edgegap.com/?oneClick=true";
public const string EDGEGAP_ADD_MORE_GAME_SERVERS_URL = "https://edgegap.com/resources/contact";
public const string EDGEGAP_DOC_BTN_HOW_TO_LOGIN_VIA_CLI_URL = "https://docs.edgegap.com/docs/container/edgegap-container-registry/#getting-your-credentials";
private const string DEFAULT_UTM_SOURCE_TAG = "plugin_unity";
private const string DEFAULT_UTM_MEDIUM_TAG = "servers_quickstart_plugin";
private const string DEFAULT_UTM_CONTENT_TAG = "plugin_button";
public const string DEFAULT_UTM_TAGS = "utm_source=" + DEFAULT_UTM_SOURCE_TAG +
"&utm_medium=" + DEFAULT_UTM_MEDIUM_TAG +
"&utm_content=" + DEFAULT_UTM_CONTENT_TAG;
public const string DEFAULT_VERSION_TAG = "latest";
public const string LOADING_RICH_STR = "<i>Loading...</i>";
public const string PROCESSING_RICH_STR = "<i>Processing...</i>";
public const string DEPLOY_REQUEST_RICH_STR = "<i>Requesting Deploy...</i>";
#region Colors
/// <summary>Earthy lime green</summary>
public const string SUCCESS_COLOR_HEX = "#8AEE8C";
/// <summary>Calming light orange</summary>
public const string WARN_COLOR_HEX = "#EEC58A";
/// <summary>Vivid blood orange</summary>
public const string FAIL_COLOR_HEX = "#EE9A8A";
/// <summary>Corn yellow</summary>
public const string PROCESSING_COLOR_HEX = "#EEEA8A";
public enum StatusColors
{
/// <summary>CornYellow</summary>
Processing,
/// <summary>EarthyLimeGreen</summary>
Success,
/// <summary>CalmingLightOrange</summary>
Warn,
/// <summary>VividBloodOrange</summary>
Error,
}
/// <returns>Wraps string in color rich text</returns>
public static string WrapRichTextInColor(string str, StatusColors statusColor)
{
switch (statusColor)
{
case StatusColors.Processing:
return $"<color={PROCESSING_COLOR_HEX}>{str}</color>";
case StatusColors.Success:
return $"<color={SUCCESS_COLOR_HEX}>{str}</color>";
case StatusColors.Warn:
return $"<color={WARN_COLOR_HEX}>{str}</color>";
case StatusColors.Error:
return $"<color={FAIL_COLOR_HEX}>{str}</color>";
default:
throw new ArgumentOutOfRangeException(nameof(statusColor), statusColor, null);
}
}
#endregion // Colors
#region Player Pref Key Ids for persistence
/// <summary>Cached as base64</summary>
public const string API_TOKEN_KEY_STR = "ApiToken";
public const string DEPLOYMENT_REQUEST_ID_KEY_STR = "DeploymentRequestId";
public const string DEPLOYMENT_CONNECTION_URL_KEY_STR = "DeploymentConnectionUrlLabel";
public const string DEPLOYMENT_CONNECTION_STATUS_KEY_STR = "DeploymentsConnectionStatusLabel";
public const string CONTAINER_REGISTRY_TRANSPORT_TYPE_ENUM_KEY_STR = "ContainerRegistryProtocolTypeEnum";
#endregion // Editor Pref Key Ids for persistence
#region UI Element Ids
public const string DEBUG_BTN_ID = "DebugBtn";
public const string API_TOKEN_TXT_ID = "ApiTokenMaskedTxt";
public const string API_TOKEN_VERIFY_BTN_ID = "ApiTokenVerifyPurpleBtn";
public const string API_TOKEN_GET_BTN_ID = "ApiTokenGetBtn";
public const string POST_AUTH_CONTAINER_ID = "PostAuthContainer";
public const string APP_INFO_FOLDOUT_ID = "ApplicationInfoFoldout";
public const string APP_NAME_TXT_ID = "ApplicationNameTxt";
public const string APP_LOAD_EXISTING_BTN_ID = "AppLoadExistingBtn";
public const string APP_ICON_SPRITE_OBJ_ID = "ApplicationIconSprite";
public const string APP_CREATE_BTN_ID = "ApplicationCreateBtn";
public const string APP_CREATE_RESULT_LABEL_ID = "ApplicationCreateResultLabel";
public const string CONTAINER_REGISTRY_FOLDOUT_ID = "ContainerRegistryFoldout";
public const string CONTAINER_REGISTRY_PORT_NUM_ID = "ContainerRegistryPortNumTxt";
public const string CONTAINER_REGISTRY_TRANSPORT_TYPE_ENUM_ID = "ContainerRegistryProtocolTypeEnumField";
public const string CONTAINER_NEW_TAG_VERSION_TXT_ID = "ContainerNewVersionTagTxt";
public const string CONTAINER_USE_CUSTOM_REGISTRY_TOGGLE_ID = "ContainerUseCustomRegistryToggle";
public const string CONTAINER_CUSTOM_REGISTRY_WRAPPER_ID = "ContainerCustomRegistryWrapper";
public const string CONTAINER_REGISTRY_URL_TXT_ID = "ContainerRegistryUrlTxt";
public const string CONTAINER_IMAGE_REPOSITORY_URL_TXT_ID = "ContainerImageRepositoryTxt";
public const string CONTAINER_USERNAME_TXT_ID = "ContainerUsernameTxt";
public const string CONTAINER_TOKEN_TXT_ID = "ContainerTokenTxt";
public const string CONTAINER_BUILD_AND_PUSH_BTN_ID = "ContainerBuildAndPushBtn";
public const string CONTAINER_BUILD_AND_PUSH_RESULT_LABEL_ID = "ContainerBuildAndPushResultLabel";
public const string DEPLOYMENTS_FOLDOUT_ID = "DeploymentsFoldout";
public const string DEPLOYMENTS_REFRESH_BTN_ID = "DeploymentsRefreshBtn";
public const string DEPLOYMENTS_CREATE_BTN_ID = "DeploymentsCreateBtn";
public const string DEPLOYMENTS_STATUS_LABEL_ID = "DeploymentsStatusLabel";
public const string DEPLOYMENTS_CONTAINER_ID = "DeploymentsConnectionGroupBox";
public const string DEPLOYMENTS_CONNECTION_COPY_URL_BTN_ID = "DeploymentConnectionCopyUrlBtn";
public const string DEPLOYMENTS_CONNECTION_URL_READONLY_TXT_ID = "DeploymentConnectionUrlReadOnlyTxt"; // Dynamic
public const string DEPLOYMENTS_CONNECTION_STATUS_LABEL_ID = "DeploymentsConnectionStatusLabel"; // Dynamic
public const string DEPLOYMENTS_CONNECTION_SERVER_ACTION_STOP_BTN_ID = "DeploymentsConnectionServerStopBtn";
public const string DEPLOYMENTS_CONNECTION_CONTAINER_LOGS_BTN_ID = "DeploymentsConnectionContainerLogsBtn";
public const string FOOTER_DOCUMENTATION_BTN_ID = "FooterDocumentationBtn";
public const string FOOTER_NEED_MORE_GAME_SERVERS_BTN_ID = "FooterNeedMoreGameServersBtn";
#endregion // UI Element Ids
//[Obsolete("Hard-coded; not from UI. TODO: Get from UI")] // MIRROR CHANGE: comment this out to avoid import warnings
public const ApiEnvironment API_ENVIRONMENT = ApiEnvironment.Console;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c17286cfb15166442920036353163fc0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c3d4497250ad3e4aa500d4c599b30fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 906ea06ae52d7f641958c4777a431af5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
Removed font files to avoid all kinds of runtime errors.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 57002d60dd8ce46b081a0a0a25e5b52b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57243654a91bff64ba14f4dd51959d32
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,140 @@
fileFormatVersion: 2
guid: caa516cdb721dd143bbc8000ca78d50a
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,96 @@
fileFormatVersion: 2
guid: b7012da4ebf9008458abc3ef9a741f3c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1255fab0d2789b47975aa6fda86b94b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,71 @@
namespace Edgegap
{
public enum ApiEnvironment
{
Staging,
Console,
}
public static class ApiEnvironmentsExtensions
{
public static string GetApiUrl(this ApiEnvironment apiEnvironment)
{
string apiUrl;
switch (apiEnvironment)
{
case ApiEnvironment.Staging:
apiUrl = "https://staging-api.edgegap.com";
break;
case ApiEnvironment.Console:
apiUrl = "https://api.edgegap.com";
break;
default:
apiUrl = null;
break;
}
return apiUrl;
}
public static string GetDashboardUrl(this ApiEnvironment apiEnvironment)
{
string apiUrl;
switch (apiEnvironment)
{
case ApiEnvironment.Staging:
apiUrl = "https://staging-console.edgegap.com";
break;
case ApiEnvironment.Console:
apiUrl = "https://console.edgegap.com";
break;
default:
apiUrl = null;
break;
}
return apiUrl;
}
public static string GetDocumentationUrl(this ApiEnvironment apiEnvironment)
{
string apiUrl;
switch (apiEnvironment)
{
case ApiEnvironment.Staging:
apiUrl = "https://staging-docs.edgegap.com/docs/category/unity";
break;
case ApiEnvironment.Console:
apiUrl = "https://docs.edgegap.com/docs/category/unity";
break;
default:
apiUrl = null;
break;
}
return apiUrl;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd1ad4f631934cc42b0bc025483f7ffc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,84 @@
using IO.Swagger.Model;
using System;
using System.Linq;
using UnityEngine;
namespace Edgegap
{
public enum ServerStatus
{
NA, // Not an actual Edgegap server status. Indicates that there are no active server.
Initializing,
Seeking,
Deploying,
Ready,
Seeked,
Terminated,
Scanning,
Terminating,
Error,
}
public static class ServerStatusExtensions
{
private static string GetServerStatusLabel(this Status serverStatusResponse) => char.ToUpper(serverStatusResponse.CurrentStatus[7]) + serverStatusResponse.CurrentStatus.Substring(8).ToLower();
public static ServerStatus GetServerStatus(this Status serverStatusResponse)
{
ServerStatus serverStatus;
try
{
serverStatus = (ServerStatus)Enum.Parse(typeof(ServerStatus), serverStatusResponse.GetServerStatusLabel());
}
catch (Exception)
{
Debug.LogError($"Got unexpected server status: {serverStatusResponse.CurrentStatus}. Considering the deployment to be terminated.");
serverStatus = ServerStatus.Terminated;
}
return serverStatus;
}
public static string GetStatusBgClass(this ServerStatus serverStatus)
{
string statusBgClass;
switch (serverStatus)
{
case ServerStatus.NA:
case ServerStatus.Terminated:
statusBgClass = "bg--secondary"; break;
case ServerStatus.Ready:
statusBgClass = "bg--success"; break;
case ServerStatus.Error:
statusBgClass = "bg--danger"; break;
default:
statusBgClass = "bg--warning"; break;
}
return statusBgClass;
}
public static string GetLabelText(this ServerStatus serverStatus)
{
string statusLabel;
if (serverStatus == ServerStatus.NA)
{
statusLabel = "N/A";
}
else
{
statusLabel = Enum.GetName(typeof(ServerStatus), serverStatus);
}
return statusLabel;
}
public static bool IsOneOf(this ServerStatus serverStatus, params ServerStatus[] serverStatusOptions)
{
return serverStatusOptions.Contains(serverStatus);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 880f675359f40d24e99109ad46894688
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
namespace Edgegap
{
public enum ToolState
{
Disconnected,
Connecting,
Connected, // Waiting for a deployment
Building,
Pushing,
ProcessingDeployment,
DeploymentRunning,
}
public static class PluginStateExtensions
{
public static bool CanConnect(this ToolState currentState)
{
return currentState == ToolState.Disconnected;
}
public static bool CanDisconnect(this ToolState currentState)
{
return currentState == ToolState.Connected;
}
public static bool CanStartDeployment(this ToolState currentState)
{
return currentState == ToolState.Connected;
}
public static bool CanStopDeployment(this ToolState currentState)
{
return currentState == ToolState.DeploymentRunning;
}
public static bool CanEditConnectionInfo(this ToolState currentState)
{
return currentState.CanConnect();
}
public static bool HasActiveDeployment(this ToolState currentState)
{
return currentState == ToolState.ProcessingDeployment || currentState == ToolState.DeploymentRunning;
}
}
}

Some files were not shown because too many files have changed in this diff Show More