├── .gitignore ├── Azalea ├── Azalea.csproj ├── Networking │ ├── Broadcast.cs │ ├── NetCommand.cs │ └── ServerDetail.cs ├── Program.cs ├── Roles │ ├── Client │ │ ├── HostHandler.cs │ │ └── NetClient.cs │ ├── Host │ │ ├── ClientHandler.cs │ │ ├── ClientPool.cs │ │ └── NetHost.cs │ └── Judger │ │ ├── HostHandler.cs │ │ └── NetJudger.cs └── app.config ├── AzaleaGUI ├── App.config ├── App.xaml ├── App.xaml.cs ├── AzaleaGUI.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── AzaleaTest ├── AzaleaTest.csproj └── Networking │ └── BroadcastTest.cs ├── LICENSE ├── README.md └── azalea.sln /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # build 5 | [Oo]bj/ 6 | [Bb]in/ 7 | packages/ 8 | TestResults/ 9 | 10 | # globs 11 | Makefile.in 12 | *.DS_Store 13 | *.sln.cache 14 | *.suo 15 | *.cache 16 | *.pidb 17 | *.userprefs 18 | *.usertasks 19 | config.log 20 | config.make 21 | config.status 22 | aclocal.m4 23 | install-sh 24 | autom4te.cache/ 25 | *.user 26 | *.tar.gz 27 | tarballs/ 28 | test-results/ 29 | Thumbs.db 30 | 31 | # Mac bundle stuff 32 | *.dmg 33 | *.app 34 | 35 | # resharper 36 | *_Resharper.* 37 | *.Resharper 38 | 39 | # dotCover 40 | *.dotCover 41 | /.vs 42 | .vs -------------------------------------------------------------------------------- /Azalea/Azalea.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp1.0 6 | Azalea 7 | azalea 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Azalea/Networking/Broadcast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Text; 7 | 8 | namespace Azalea.Networking 9 | { 10 | public class Broadcast 11 | { 12 | private const int DefaultListenPort = 6391; 13 | private const int BindPort = 6392; 14 | private const int BroadcastDelay = 200; 15 | private const int SeekTimeout = 3000; 16 | 17 | private IPEndPoint SendEndpoint; 18 | private IPEndPoint ListenEndpoint; 19 | 20 | private static Broadcast instance = new Broadcast(); 21 | public static Broadcast Instance => instance; 22 | 23 | public Broadcast() 24 | { 25 | SendEndpoint = new IPEndPoint(IPAddress.Broadcast, BindPort); 26 | ListenEndpoint = new IPEndPoint(IPAddress.Any, BindPort); 27 | } 28 | 29 | private async Task Send(ServerDetail data) 30 | { 31 | var client = new UdpClient(); 32 | var dataString = data.Serialize(); 33 | var bytes = Encoding.ASCII.GetBytes(dataString); 34 | await client.SendAsync(bytes, bytes.Length, SendEndpoint); 35 | } 36 | 37 | private async Task Receive() 38 | { 39 | var client = new UdpClient(ListenEndpoint); 40 | var asyncData = await client.ReceiveAsync(); 41 | var data = Encoding.ASCII.GetString(asyncData.Buffer); 42 | try 43 | { 44 | return ServerDetail.Unserialize(data); 45 | } 46 | catch (FormatException) 47 | { 48 | return null; 49 | } 50 | } 51 | 52 | public async Task GetServer() 53 | { 54 | var task = Receive(); 55 | if (await Task.WhenAny(task, Task.Delay(SeekTimeout)) == task) 56 | { 57 | return task.Result; 58 | } 59 | else 60 | { 61 | return null; 62 | } 63 | } 64 | 65 | private void SendTimer(Object data) 66 | { 67 | var ServerDetail = (ServerDetail)data; 68 | var task = Send(ServerDetail); 69 | } 70 | 71 | public Timer StartBroadcast(ServerDetail detail) 72 | { 73 | var timer = new Timer(this.SendTimer, detail, 0, BroadcastDelay); 74 | return timer; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Azalea/Networking/NetCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Azalea.Networking 5 | { 6 | public enum HostCommandType 7 | { 8 | Register, 9 | Disconnect, 10 | GenericCommandSuccess, 11 | InvalidCommand 12 | } 13 | 14 | public enum ClientCommandType 15 | { 16 | GenericCommandSuccess, 17 | InvalidCommand 18 | } 19 | 20 | public enum JudgerCommandType 21 | { 22 | GenericCommandSuccess, 23 | InvalidCommand 24 | } 25 | 26 | public class NetCommand 27 | { 28 | 29 | private CommandType Command; 30 | public string CommandString 31 | { 32 | get 33 | { 34 | return Command.ToString(); 35 | } 36 | private set 37 | { 38 | Command = (CommandType)Enum.Parse(typeof(CommandType), value); 39 | } 40 | } 41 | 42 | private Object[] parameters; 43 | public Object[] Parameters 44 | { 45 | get 46 | { 47 | return parameters; 48 | } 49 | private set 50 | { 51 | parameters = value; 52 | } 53 | } 54 | 55 | public NetCommand(CommandType command, params Object[] param) 56 | { 57 | parameters = param; 58 | Command = command; 59 | } 60 | 61 | private NetCommand(string commandString, Object[] param) 62 | { 63 | parameters = param; 64 | CommandString = commandString; 65 | } 66 | 67 | public string Serialize() 68 | { 69 | return JsonConvert.SerializeObject(Parameters); 70 | } 71 | 72 | public static NetCommand Unserialize(string CommandJson) 73 | { 74 | return JsonConvert.DeserializeObject>(CommandJson); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Azalea/Networking/ServerDetail.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Collections.Generic; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace Azalea.Networking 7 | { 8 | public class ServerDetail 9 | { 10 | private string name; 11 | public string Name 12 | { 13 | get 14 | { 15 | return name; 16 | } 17 | private set 18 | { 19 | if (value.Length == 0) 20 | { 21 | throw new ArgumentNullException(); 22 | } 23 | 24 | if (!Regex.IsMatch(value, @"^[a-zA-Z0-9\-_]{3,15}$")) 25 | { 26 | throw new FormatException("Invalid Name"); 27 | } 28 | 29 | name = value; 30 | } 31 | } 32 | 33 | private string identifier; 34 | public string Identifier 35 | { 36 | get 37 | { 38 | return identifier; 39 | } 40 | private set 41 | { 42 | if (!Regex.IsMatch(value, @"^[A-F0-9]{12}$")) 43 | { 44 | throw new FormatException("Invalid Identifier"); 45 | } 46 | 47 | identifier = value; 48 | } 49 | } 50 | 51 | public IPEndPoint EndPoint; 52 | public IPAddress IPAddress 53 | { 54 | get 55 | { 56 | return EndPoint.Address; 57 | } 58 | private set 59 | { 60 | EndPoint.Address = value; 61 | } 62 | } 63 | public int Port 64 | { 65 | get 66 | { 67 | return EndPoint.Port; 68 | } 69 | private set 70 | { 71 | EndPoint.Port = value; 72 | } 73 | } 74 | 75 | public ServerDetail(string _name, string _identifier, IPEndPoint _endPoint) 76 | { 77 | Name = _name; 78 | Identifier = _identifier; 79 | EndPoint = _endPoint; 80 | } 81 | 82 | public ServerDetail(string _name, string _identifier, string ipAddress, int port) : 83 | this(_name, _identifier, new IPEndPoint(System.Net.IPAddress.Parse(ipAddress), port)) 84 | { 85 | } 86 | 87 | public string Serialize() 88 | { 89 | var stringArray = new List { Name, Identifier, IPAddress.ToString(), Port.ToString() }; 90 | return String.Join(";", stringArray); 91 | } 92 | 93 | public static ServerDetail Unserialize(string data) 94 | { 95 | var stringArray = data.Split(';'); 96 | if (stringArray.Length != 4) 97 | { 98 | throw new FormatException("Invalid ServerDetail"); 99 | } 100 | 101 | int port; 102 | if (!Int32.TryParse(stringArray[3], out port)) 103 | { 104 | throw new FormatException("Port in ServerDetail is not a valid Int"); 105 | } 106 | 107 | return new ServerDetail(stringArray[0], stringArray[1], stringArray[2], port); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Azalea/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Extensions.CommandLineUtils; 4 | using Azalea.Networking; 5 | 6 | namespace Azalea 7 | { 8 | internal static class Program 9 | { 10 | private static CommandLineApplication app; 11 | 12 | static void Main(string[] args) 13 | { 14 | app = new CommandLineApplication 15 | { 16 | Name = "azalea", 17 | Description = "AZALEA by Luogu", 18 | FullName = "AZALEA" 19 | }; 20 | 21 | SetupApp(); 22 | 23 | app.Execute(args); 24 | } 25 | 26 | static int HostCommand() 27 | { 28 | var broadcast = Broadcast.Instance; 29 | var dummyServer = new ServerDetail("Dummy", "001122AABBCC", "127.0.0.1", 2333); 30 | broadcast.StartBroadcast(dummyServer); 31 | while (true) { } 32 | return 0; 33 | } 34 | 35 | static int ClientCommand() 36 | { 37 | var broadcast = Broadcast.Instance; 38 | var task = broadcast.GetServer(); 39 | task.Wait(); 40 | var server = task.Result; 41 | Console.WriteLine(server.Name); 42 | return 0; 43 | } 44 | 45 | static int JudgerCommand() 46 | { 47 | 48 | return 0; 49 | } 50 | 51 | private static void SetupApp() 52 | { 53 | app.HelpOption("-? | --help"); 54 | var verbose = app.Option("-v | --verbose", "Verbose output", CommandOptionType.NoValue); 55 | var quiet = app.Option("-q | --quiet", "Silence output", CommandOptionType.NoValue); 56 | 57 | app.Command("host", config => 58 | { 59 | config.Description = "Run this instance as host"; 60 | config.HelpOption("-? | --help"); 61 | var bind = config.Option("-l | --listen
", "Listen on the specified address", CommandOptionType.SingleValue); 62 | var noBroadcast = config.Option("--no-broadcast
", "Do not broadcast this server", CommandOptionType.NoValue); 63 | config.OnExecute(() => HostCommand()); 64 | }); 65 | 66 | app.Command("client", config => 67 | { 68 | config.Description = "Run this instance as client"; 69 | config.HelpOption("-? | --help"); 70 | var host = config.Option("-h | --host
", "Connect to the specified host", CommandOptionType.SingleValue); 71 | var noDiscovery = config.Option("--no-discovery
", "Do not auto discover server", CommandOptionType.NoValue); 72 | config.OnExecute(() => ClientCommand()); 73 | }); 74 | 75 | app.Command("judger", config => 76 | { 77 | config.Description = "Run this instance as judger"; 78 | config.HelpOption("-? | --help"); 79 | var host = config.Option("-h | --host
", "Connect to the specified host", CommandOptionType.SingleValue); 80 | var noDiscovery = config.Option("--no-discovery
", "Do not auto discover server", CommandOptionType.NoValue); 81 | config.OnExecute(() => JudgerCommand()); 82 | }); 83 | 84 | app.OnExecute(() => 85 | { 86 | app.ShowHelp(); 87 | return 0; 88 | }); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Azalea/Roles/Client/HostHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Azalea.Networking; 3 | 4 | namespace Azalea.Roles.Client 5 | { 6 | public class HostHandler 7 | { 8 | private ServerDetail Detail; 9 | 10 | public HostHandler(ServerDetail detail) 11 | { 12 | Detail = detail; 13 | } 14 | 15 | public void GenericCommandSuccess(string CommandName) 16 | { 17 | 18 | } 19 | 20 | public void InvalidCommand() 21 | { 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Azalea/Roles/Client/NetClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using System.Net.Sockets; 6 | using System.Reflection; 7 | using Azalea.Networking; 8 | 9 | namespace Azalea.Roles.Client 10 | { 11 | public class NetClient 12 | { 13 | private const int MaxBuffer = 51200; 14 | 15 | private string name; 16 | public string Name 17 | { 18 | get 19 | { 20 | return name; 21 | } 22 | private set 23 | { 24 | if (value.Length == 0 || value.Length > 20) 25 | { 26 | throw new ArgumentException("Name must be between 1-20 chars long"); 27 | } 28 | 29 | name = value; 30 | } 31 | } 32 | 33 | private string identifier; 34 | public string Identifier 35 | { 36 | get 37 | { 38 | return identifier; 39 | } 40 | private set 41 | { 42 | if (!Regex.IsMatch(value, @"^[A-F0-9]{12}$")) 43 | { 44 | throw new FormatException("Invalid Identifier"); 45 | } 46 | 47 | identifier = value; 48 | } 49 | } 50 | 51 | private ServerDetail HostDetail; 52 | private TcpClient Connection; 53 | private HostHandler handler; 54 | public HostHandler Handler => handler; 55 | 56 | private bool IsRunning; 57 | 58 | private static NetClient instance; 59 | public static NetClient Instance => instance; 60 | 61 | public NetClient(ServerDetail hostDetail, string _name, string _identifier) 62 | { 63 | instance = this; 64 | HostDetail = hostDetail; 65 | Name = _name; 66 | Identifier = _identifier; 67 | } 68 | 69 | public async Task Connect() 70 | { 71 | Connection = new TcpClient(); 72 | await Connection.ConnectAsync(HostDetail.EndPoint.Address, HostDetail.EndPoint.Port); 73 | 74 | await CommandHost(new NetCommand(HostCommandType.Register, Name, Identifier)); 75 | var hostComm = HostCommunication(); 76 | 77 | handler = new HostHandler(HostDetail); 78 | 79 | IsRunning = true; 80 | return Connection; 81 | } 82 | 83 | private async Task HostCommunication() 84 | { 85 | var ReadBuffer = new Byte[MaxBuffer]; 86 | while (IsRunning) 87 | { 88 | await Connection.GetStream().ReadAsync(ReadBuffer, 0, MaxBuffer); 89 | var commandJson = Encoding.UTF8.GetString(ReadBuffer); 90 | 91 | NetCommand command; 92 | try 93 | { 94 | command = NetCommand.Unserialize(commandJson); 95 | } 96 | catch (ArgumentException) 97 | { 98 | _ = CommandHost(new NetCommand(HostCommandType.InvalidCommand)); 99 | continue; 100 | } 101 | 102 | _ = InvokeCommand(command); 103 | } 104 | } 105 | 106 | public void Terminate() 107 | { 108 | IsRunning = false; 109 | } 110 | 111 | public async Task CommandHost(NetCommand command) 112 | { 113 | var payload = command.Serialize(); 114 | var buffer = Encoding.UTF8.GetBytes(payload); 115 | await Connection.GetStream().WriteAsync(buffer, 0, buffer.Length); 116 | } 117 | 118 | private Task InvokeCommand(NetCommand command) 119 | { 120 | var method = typeof(HostHandler).GetMethod(command.CommandString); 121 | return Task.Run(() => method.Invoke(Handler, command.Parameters)); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Azalea/Roles/Host/ClientHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using System.Reflection; 4 | using Azalea.Networking; 5 | 6 | namespace Azalea.Roles.Host 7 | { 8 | public class ClientHandler 9 | { 10 | private string name; 11 | public string Name 12 | { 13 | get 14 | { 15 | return name; 16 | } 17 | private set 18 | { 19 | if (value.Length == 0 || value.Length > 20) 20 | { 21 | throw new ArgumentException("Name must be between 1-20 chars long"); 22 | } 23 | 24 | name = value; 25 | } 26 | } 27 | 28 | private string identifier; 29 | public string Identifier 30 | { 31 | get 32 | { 33 | return identifier; 34 | } 35 | private set 36 | { 37 | if (!Regex.IsMatch(value, @"^[A-F0-9]{12}$")) 38 | { 39 | throw new FormatException("Invalid Identifier"); 40 | } 41 | 42 | identifier = value; 43 | } 44 | } 45 | 46 | private NetHost.HostClient HostClient; 47 | 48 | public ClientHandler(NetHost.HostClient hostClient) 49 | { 50 | HostClient = hostClient; 51 | } 52 | 53 | public async void Register(string _name, string _identifier) 54 | { 55 | Name = _name; 56 | Identifier = _identifier; 57 | 58 | await HostClient.CommandClient(new NetCommand(ClientCommandType.GenericCommandSuccess, "Register")); 59 | } 60 | 61 | public void Disconnect() 62 | { 63 | HostClient.Terminate(); 64 | } 65 | 66 | public void GenericCommandSuccess(string CommandName) 67 | { 68 | 69 | } 70 | 71 | public void InvalidCommand() 72 | { 73 | 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Azalea/Roles/Host/ClientPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Azalea.Networking; 4 | 5 | namespace Azalea.Roles.Host 6 | { 7 | public class ClientPool 8 | { 9 | private List pool; 10 | public List Pool => pool; 11 | 12 | public ClientPool() 13 | { 14 | } 15 | 16 | public bool AddClient(NetHost.HostClient client) 17 | { 18 | if (!Pool.Contains(client)) 19 | { 20 | Pool.Add(client); 21 | return true; 22 | } 23 | else 24 | { 25 | return false; 26 | } 27 | } 28 | 29 | public void BroadcastInvoke(NetCommand command) 30 | { 31 | foreach (var client in Pool) 32 | { 33 | var commandInvoke = client.CommandClient(command); 34 | } 35 | } 36 | 37 | public bool RemoveClient(NetHost.HostClient client) 38 | { 39 | if (Pool.Contains(client)) 40 | { 41 | Pool.Remove(client); 42 | return true; 43 | } 44 | else 45 | { 46 | return false; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Azalea/Roles/Host/NetHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading.Tasks; 5 | using System.Text; 6 | using System.Reflection; 7 | using Azalea.Networking; 8 | 9 | namespace Azalea.Roles.Host 10 | { 11 | public class NetHost 12 | { 13 | private const int MaxBuffer = 51200; 14 | 15 | private ServerDetail Detail; 16 | private TcpListener Listener; 17 | private Task AcceptClientTask; 18 | public ClientPool Pool; 19 | private bool IsRunning; 20 | 21 | private static NetHost instance; 22 | public static NetHost Instance => instance; 23 | 24 | public class HostClient { 25 | private TcpClient connection; 26 | public TcpClient Connection => connection; 27 | public IPEndPoint EndPoint 28 | { 29 | get 30 | { 31 | return (IPEndPoint)Connection.Client.RemoteEndPoint; 32 | } 33 | } 34 | 35 | private ClientHandler handler; 36 | public ClientHandler Handler => handler; 37 | 38 | private bool IsRunning = true; 39 | 40 | public HostClient(TcpClient Conn) 41 | { 42 | connection = Conn; 43 | handler = new ClientHandler(this); 44 | } 45 | 46 | public void Terminate() 47 | { 48 | IsRunning = false; 49 | NetHost.Instance.Pool.RemoveClient(this); 50 | } 51 | 52 | public async Task CommandClient(NetCommand command) 53 | { 54 | var payload = command.Serialize(); 55 | var buffer = Encoding.UTF8.GetBytes(payload); 56 | await connection.GetStream().WriteAsync(buffer, 0, buffer.Length); 57 | } 58 | 59 | private Task InvokeCommand(NetCommand command) 60 | { 61 | var method = typeof(ClientHandler).GetMethod(command.CommandString); 62 | return Task.Run(() => method.Invoke(Handler, command.Parameters)); 63 | } 64 | 65 | public async Task ServeClient() 66 | { 67 | var ReadBuffer = new Byte[MaxBuffer]; 68 | while (IsRunning) 69 | { 70 | await Connection.GetStream().ReadAsync(ReadBuffer, 0, MaxBuffer); 71 | var commandJson = Encoding.UTF8.GetString(ReadBuffer); 72 | 73 | NetCommand command; 74 | try 75 | { 76 | command = NetCommand.Unserialize(commandJson); 77 | } 78 | catch (ArgumentException) 79 | { 80 | _ = CommandClient(new NetCommand(ClientCommandType.InvalidCommand)); 81 | continue; 82 | } 83 | 84 | _ = InvokeCommand(command); 85 | } 86 | } 87 | } 88 | 89 | public NetHost(ServerDetail detail) 90 | { 91 | instance = this; 92 | Detail = detail; 93 | 94 | Listener = new TcpListener(Detail.EndPoint); 95 | Listener.Start(); 96 | 97 | IsRunning = true; 98 | AcceptClientTask = Task.Run(AcceptClient); 99 | } 100 | 101 | public async Task AcceptClient() 102 | { 103 | while (IsRunning) 104 | { 105 | var clientConn = await Listener.AcceptTcpClientAsync(); 106 | var client = new HostClient(clientConn); 107 | Pool.AddClient(client); 108 | var task = client.ServeClient(); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Azalea/Roles/Judger/HostHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Azalea.Networking; 3 | 4 | namespace Azalea.Roles.Judger 5 | { 6 | public class HostHandler 7 | { 8 | private ServerDetail Detail; 9 | 10 | public HostHandler(ServerDetail detail) 11 | { 12 | Detail = detail; 13 | } 14 | 15 | public void GenericCommandSuccess(string CommandName) 16 | { 17 | 18 | } 19 | 20 | public void InvalidCommand() 21 | { 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Azalea/Roles/Judger/NetJudger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using System.Net.Sockets; 6 | using System.Reflection; 7 | using Azalea.Networking; 8 | 9 | namespace Azalea.Roles.Judger 10 | { 11 | public class NetJudger 12 | { 13 | private const int MaxBuffer = 51200; 14 | 15 | private string name; 16 | public string Name 17 | { 18 | get 19 | { 20 | return name; 21 | } 22 | private set 23 | { 24 | if (value.Length == 0 || value.Length > 20) 25 | { 26 | throw new ArgumentException("Name must be between 1-20 chars long"); 27 | } 28 | 29 | name = value; 30 | } 31 | } 32 | 33 | private string identifier; 34 | public string Identifier 35 | { 36 | get 37 | { 38 | return identifier; 39 | } 40 | private set 41 | { 42 | if (!Regex.IsMatch(value, @"^[A-F0-9]{12}$")) 43 | { 44 | throw new FormatException("Invalid Identifier"); 45 | } 46 | 47 | identifier = value; 48 | } 49 | } 50 | 51 | private ServerDetail HostDetail; 52 | private TcpClient Connection; 53 | private HostHandler handler; 54 | public HostHandler Handler => handler; 55 | 56 | private bool IsRunning; 57 | 58 | private static NetJudger instance; 59 | public static NetJudger Instance => instance; 60 | 61 | public NetJudger(ServerDetail hostDetail, string _name, string _identifier) 62 | { 63 | instance = this; 64 | HostDetail = hostDetail; 65 | Name = _name; 66 | Identifier = _identifier; 67 | } 68 | 69 | public async Task Connect() 70 | { 71 | Connection = new TcpClient(); 72 | await Connection.ConnectAsync(HostDetail.EndPoint.Address, HostDetail.EndPoint.Port); 73 | 74 | await CommandHost(new NetCommand(HostCommandType.Register, Name, Identifier)); 75 | var hostComm = HostCommunication(); 76 | 77 | handler = new HostHandler(HostDetail); 78 | 79 | IsRunning = true; 80 | return Connection; 81 | } 82 | 83 | private async Task HostCommunication() 84 | { 85 | var ReadBuffer = new Byte[MaxBuffer]; 86 | while (IsRunning) 87 | { 88 | await Connection.GetStream().ReadAsync(ReadBuffer, 0, MaxBuffer); 89 | var commandJson = Encoding.UTF8.GetString(ReadBuffer); 90 | 91 | NetCommand command; 92 | try 93 | { 94 | command = NetCommand.Unserialize(commandJson); 95 | } 96 | catch (ArgumentException) 97 | { 98 | _ = CommandHost(new NetCommand(HostCommandType.InvalidCommand)); 99 | continue; 100 | } 101 | 102 | _ = InvokeCommand(command); 103 | } 104 | } 105 | 106 | public void Terminate() 107 | { 108 | IsRunning = false; 109 | } 110 | 111 | public async Task CommandHost(NetCommand command) 112 | { 113 | var payload = command.Serialize(); 114 | var buffer = Encoding.UTF8.GetBytes(payload); 115 | await Connection.GetStream().WriteAsync(buffer, 0, buffer.Length); 116 | } 117 | 118 | private Task InvokeCommand(NetCommand command) 119 | { 120 | var method = typeof(HostHandler).GetMethod(command.CommandString); 121 | return Task.Run(() => method.Invoke(Handler, command.Parameters)); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Azalea/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /AzaleaGUI/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AzaleaGUI/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /AzaleaGUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace AzaleaGUI 10 | { 11 | /// 12 | /// App.xaml 的交互逻辑 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /AzaleaGUI/AzaleaGUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A838D527-B164-4C89-A705-AF9BBAF7379D} 8 | WinExe 9 | AzaleaGUI 10 | AzaleaGUI 11 | v4.5.2 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 4.0 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | MSBuild:Compile 55 | Designer 56 | 57 | 58 | MSBuild:Compile 59 | Designer 60 | 61 | 62 | App.xaml 63 | Code 64 | 65 | 66 | MainWindow.xaml 67 | Code 68 | 69 | 70 | 71 | 72 | Code 73 | 74 | 75 | True 76 | True 77 | Resources.resx 78 | 79 | 80 | True 81 | Settings.settings 82 | True 83 | 84 | 85 | ResXFileCodeGenerator 86 | Resources.Designer.cs 87 | 88 | 89 | SettingsSingleFileGenerator 90 | Settings.Designer.cs 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /AzaleaGUI/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /AzaleaGUI/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace AzaleaGUI 17 | { 18 | /// 19 | /// MainWindow.xaml 的交互逻辑 20 | /// 21 | public partial class MainWindow : Window 22 | { 23 | public MainWindow() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AzaleaGUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("AzaleaGUI")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("AzaleaGUI")] 15 | [assembly: AssemblyCopyright("Copyright © 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //例如,如果您在源文件中使用的是美国英语, 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 52 | // 方法是按如下所示使用“*”: : 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /AzaleaGUI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.42000 5 | // 6 | // 对此文件的更改可能导致不正确的行为,如果 7 | // 重新生成代码,则所做更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AzaleaGUI.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 强类型资源类,用于查找本地化字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的缓存 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AzaleaGUI.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 覆盖当前线程的 CurrentUICulture 属性 56 | /// 使用此强类型的资源类的资源查找。 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AzaleaGUI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /AzaleaGUI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AzaleaGUI.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AzaleaGUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /AzaleaTest/AzaleaTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp1.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /AzaleaTest/Networking/BroadcastTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Azalea.Networking; 4 | 5 | namespace AzaleaTest.Networking 6 | { 7 | public class BroadcastTest 8 | { 9 | private Broadcast broadcast; 10 | 11 | public BroadcastTest() 12 | { 13 | broadcast = Broadcast.Instance; 14 | } 15 | 16 | [Fact] 17 | public void Test() { 18 | var dummyServer = new ServerDetail("Dummy", "001122AABBCC", "127.0.0.1", 2333); 19 | broadcast.StartBroadcast(dummyServer); 20 | 21 | var task = broadcast.GetServer(); 22 | task.Wait(); 23 | var receivedServer = task.Result; 24 | 25 | Assert.Equal(dummyServer, receivedServer); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project AZALEA 2 | AZALEA: A Zero Anti-human Local program Evaluator Application 3 | 4 | By Luogu 5 | 6 | AZALEA是洛谷的教室内比赛、作业、考试客户端解决方案,计划功能上是同类型软件Cena的超集,但却又要极其易用且可靠,并且与洛谷的各项服务紧密结合。 7 | 8 | 计划特性列表: 9 | * 内核使用C语言和.NET Core编写,GUI基于WPF 10 | * 跨Windows、macOS、Linux三大平台(GUI仅Windows) 11 | * 分为服务端、评测端、客户端三部分 12 | * 支持本地评测机评测和提交到洛谷云端评测 13 | * 支持一键导入洛谷比赛和把创建的比赛、题目上传到洛谷 14 | * 支持绑定洛谷账号 15 | * 支持OI赛制、ACM赛制、乐多赛制、IOI赛制、Codeforces赛制,除了OI赛制以外学生可以在自己客户端上看到成绩 16 | * 支持提交答案、SPJ 17 | * 支持安全评测(不支持macOS) 18 | * 支持添加题面等附加信息并发送到客户端显示 19 | * 支持在广域网上比赛,同局域网内还可自动发现服务端 20 | * 支持把评测后的成绩上传到洛谷的比赛,其中客户端有登陆洛谷账号的成绩显示为相应账号,未绑定的显示成本机机器名 21 | -------------------------------------------------------------------------------- /azalea.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26403.7 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azalea", "azalea\Azalea.csproj", "{464C87B4-F730-4369-BF5F-B34A913A3E67}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzaleaGUI", "AzaleaGUI\AzaleaGUI.csproj", "{A838D527-B164-4C89-A705-AF9BBAF7379D}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzaleaTest", "AzaleaTest\AzaleaTest.csproj", "{6DE27A43-00C0-4127-B825-CFADC48923D2}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Release|Any CPU = Release|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {464C87B4-F730-4369-BF5F-B34A913A3E67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {464C87B4-F730-4369-BF5F-B34A913A3E67}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {464C87B4-F730-4369-BF5F-B34A913A3E67}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {464C87B4-F730-4369-BF5F-B34A913A3E67}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {A838D527-B164-4C89-A705-AF9BBAF7379D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {A838D527-B164-4C89-A705-AF9BBAF7379D}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {A838D527-B164-4C89-A705-AF9BBAF7379D}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {A838D527-B164-4C89-A705-AF9BBAF7379D}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {6DE27A43-00C0-4127-B825-CFADC48923D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {6DE27A43-00C0-4127-B825-CFADC48923D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {6DE27A43-00C0-4127-B825-CFADC48923D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {6DE27A43-00C0-4127-B825-CFADC48923D2}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | EndGlobal 34 | --------------------------------------------------------------------------------