├── .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 |
--------------------------------------------------------------------------------