Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22

Task вызов

08.04.2018, 21:33. Показов 2338. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!
Сейчас разбираюсь с асинхронностью и тут встал вопрос.
есть функция -

C#
1
2
3
4
private static async Task ClientService(Client client, CancellationToken ct)
{
    await ReadAsync(client, ct);
}
ee можно вызвать так -
C#
1
var task = ClientService(client, cts.Token);
и так -
C#
1
var task = Task.Run(() => ClientService(client, cts.Token));
Вопрос! в чем разница? и как правильнее ?

или может так ?
C#
1
2
3
4
private static void ClientService(Client client, CancellationToken ct)
{
     var task = Task.Run(() => ReadAsync(client, cts.Token));
}
и вызвать просто -
C#
1
ClientService(client, cts.Token);
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
08.04.2018, 21:33
Ответы с готовыми решениями:

Вызов событий из Task
Добрый день. Как мне вызвать event из Task? Дело в том что Taks в качестве параметра принимает только статичный метод, а из...

Task.WhenAll для разных типов Task
Здравствуйте! Есть код вида SomeType a = await SomeTask; OtherType b = await OtherTask; //other code Как можно эти два await'a...

Отмена одного Task в массиве Task
Как можно отменить одну задачу в массиве Task? Никак же нельзя передать CancellationTokenSource в массиве?

14
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
08.04.2018, 21:58
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
в чем разница?
В первом случае запускается асинхронная операция и возвращается ссылка на объект, ее представляющий.
Во втором случае запускается асинхронная операция, которая запускает асинхронную операцию и возвращает ссылку на объект, представляющий асинхронную операцию, которая возвращает асинхронную операцию. Масло-масляное, короче.

Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
как правильнее ?
Если выбирать из двух, то первый вариант.
Первый вариант.

Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
или может так ?
Не.
1
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
09.04.2018, 08:20  [ТС]
Спасибо за ответ!
Цитата Сообщение от kolorotur Посмотреть сообщение
Если выбирать из двух, то первый вариант.
а есть еще лучше варианты? я только учусь был бы рад узнать
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
09.04.2018, 09:01
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
а есть еще лучше варианты?
Поскольку в первом варианте у вас в методе ничего кроме асинхронного ожидания не происходит, было бы логичнее просто возвращать Task, а не создавать конечный автомат, ожидающий завершения задачи (это делается при использовании конструкции await) только для того, чтобы тут же завершить работу метода:
C#
1
2
3
4
private static Task ClientService(Client client, CancellationToken ct)
{
    return ReadAsync(client, ct);
}
0
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
09.04.2018, 09:33  [ТС]
а ну тут не совсем так, там у меня цикл
C#
1
2
3
4
5
6
while (!cts.IsCancellationRequested)
                {
                    Console.WriteLine("Waiting for a connection...");
                    var client = await AcceptClient();
                    var task = ClientService(client, cts.Token);
                }
и мне надо чтоб цикл прошел итерацию чтоб другие смогли тоже приконнектиться

просто я честно даже не знаю что делать с task который создается от ClientService а сделать
C#
1
2
3
4
private static async void ClientService(Client client, CancellationToken ct)
{
    return ReadAsync(client, ct);
}
async Void вроде как не желательно...
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
09.04.2018, 09:40
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
тут не совсем так
На будущее: лучше все-таки для конкретного ответа выкладывать конкретный код, а не гипотетический пример
Особенно если нет четкого понимания происходящего (а его нет, иначе бы темы не было!)
Разница может быть существенная, в чем вы убедитесь, дочитав ответ до конца.

Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
там у меня цикл
Вот в этом варианте я бы уже предложил запускать ClientService через Task.Run — как во втором примере, поскольку асинхронный вызов не гарантирует многопоточности, а Task.Run запускает выполнение в одном из потоков пула.
Если оставить код как у вас, то возможна ситуация при которой следующий клиент не подключится до тех пор, пока не прекратится работа с последним подключившимся. Вероятность мала, но не нулевая.

Не совсем по теме, но я бы так же предложил передавать CancellationToken в метода AcceptClient, а то сейчас при отмене у вас цикл не прекратится до следующего подключения клиента, которое может никогда не произойти.
2
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
09.04.2018, 15:02  [ТС]
Большое спасибо за советы, Вы очень помогли

Добавлено через 58 минут
Цитата Сообщение от kolorotur Посмотреть сообщение
я бы так же предложил передавать CancellationToken в метода AcceptClient
C#
1
2
3
4
5
6
7
8
9
10
11
private static async Task<Client> AcceptClient(CancellationToken ct)
{
     var client = new Client();
     var socket = await Task<Socket>.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).ConfigureAwait(false);;
     client.workSocket = socket;
     client.id = clients.Count;
     client.ip = socket.RemoteEndPoint.ToString();
     clients.Add(client);
     Console.WriteLine("Clients count is: " + clients.Count + " IP: " + client.ip);
     return client;
}
А не подскажите куда именно вставить, токен отмены?

Добавлено через 1 час 3 минуты
Цитата Сообщение от kolorotur Посмотреть сообщение
CancellationToken в метода AcceptClient
блин я так понял что это сделать нельзя?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
10.04.2018, 10:31
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
куда именно вставить, токен отмены?
Поскольку вы используете обертку над асинхронными вызовами, напрямую токен передать не получится — придется проверять после.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
private static async Task<Client> AcceptClient(CancellationToken ct)
{
     var socket = await Task<Socket>.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).ConfigureAwait(false);
     ct.ThrowIfCancellationRequested();
 
     var client = new Client();
     client.workSocket = socket;
     client.id = clients.Count;
     client.ip = socket.RemoteEndPoint.ToString();
     clients.Add(client);
     Console.WriteLine("Clients count is: " + clients.Count + " IP: " + client.ip);
     return client;
}
Заодно зарегистрируйте отмену операции на закрытие сервера:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var token = cts.Token;
using (var registration = token.Register(client.Close))
{
   while (!token.IsCancellationRequested)
   {
      Console.WriteLine("Waiting for a connection...");
      try
      {
         var client = await AcceptClient();
         var task = ClientService(client, token);
      }
      catch (OperationCancelledException) { break; }
      catch (ObjectDisposedException) when (token.IsCancellationRequested) { break; }
   }
}
Добавлено через 22 секунды
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
блин я так понял что это сделать нельзя?
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
Добавлено через 1 час 3 минуты
Люди же работают, не всегда есть время на форуме отдыхать.
1
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
10.04.2018, 17:14  [ТС]
Спасибо за ответы
Цитата Сообщение от kolorotur Посмотреть сообщение
token.Register(client.Close)
а что за client.Close Вы сюда передали? tcpClient?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
10.04.2018, 17:23
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
а что за client.Close Вы сюда передали? tcpClient?
Да, он самый.
При отмене будет вызван метод Close на "слушателе" портов, вызов которого приведет к прерыванию асинхронного BeginAccept и выбросу исключения.
Не помню точно, какое исключение бросается в этом случае: ObjectDisposedException или SocketException (поэкспериментируйте!), но это исключение отлавливается и цикл завершается.
0
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
10.04.2018, 17:27  [ТС]
у меня правда там нет tcpClient там просто слушаю сокет я так понимаю мне нужно вызвать socket.Close?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 using (var registration = cts.Token.Register(() => listener.Close()))
            {
                while (!cts.IsCancellationRequested)
                {
                    Console.WriteLine("Waiting for a connection...");
                    try
                    {
                        var client = await AcceptClient(cts.Token);
                        var task = Task.Run(() => ClientService(client, cts.Token));
                    }
                    catch (OperationCanceledException) { break; }
                    catch (ObjectDisposedException) when (cts.Token.IsCancellationRequested) { break; }
                }
            }
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
10.04.2018, 17:30
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
я так понимаю мне нужно вызвать socket.Close?
Ну да, это я имена переменных перепутал.
Должно быть так:
C#
1
using (var registration = token.Register(listener.Close))
Лямбду создавать не обязательно, т.к. сигнатуры совпадают.
0
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
10.04.2018, 18:22  [ТС]
я очень много от Вас понял т.к. получил инфу и понял что нужно почитать Спсибо!
наверно я много прошу но может быть вы посмотрите код всего сервера и дадите еще какие нить советы?
конечно если не затруднит...
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Database;
using MongoScripts;
using Microservices;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using System.Collections.Concurrent;
using Server;
using System.IO;
 
namespace Server
{
    public class Client
    { 
        public Socket socket = null;        
        public int id { get; set; }
        public string userId { get; set; }
        public string ip { get; set; }
        public string authToken { get; set; }
        public string characterId { get; set; }
        //Size of receive buffer.
        public const int BufferSize = 1000000;
        //Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        //Received data string.
        public StringBuilder sb = new StringBuilder();  
    }
 
    public static class Connector
    {
        public static List<Client> clients = new List<Client>();
        private static CancellationTokenSource cts;
        private static Socket listener;
 
        /// <summary>
        /// Start tcp server
        /// </summary>
        public static async Task StartListening()
        {  
            cts = new CancellationTokenSource();      
 
            // IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
            // IPAddress ipAddress = ipHostInfo.AddressList[0];
            // IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
 
            //Establish the endpoint for the socket.
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 1121);
 
            //Create a TCP/IP socket.
            listener = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);
            Console.WriteLine(@"  
            ===================================================  
                   Started listening requests at: {0}:{1}  
            ===================================================",   
            endPoint.Address, endPoint.Port);  
            try 
            {
                //Bind the socket to the local endpoint and listen for incoming connections.
                listener.Bind(endPoint);
                listener.Listen(100);
 
                DbConnect.Connect();
                // var monstersGenerator = new MonstersGenerator();
                // var skillsGenerator = new SkillsGenerator();
                // var eliteMonstersGenerator = new EliteMonstersGenerator();
                // var bosses = new BossesGenerator();
                // var locationGenerator = new LocationGenerator();
                // ServerGenerator.ServerGeneratorAsync("Server3");
                // CharacterClassesGenerator.CharacterClassesGeneratorAsync();
                // monstersGenerator.GenerateMonster();
                // skillsGenerator.GenerateSkills();
                // eliteMonstersGenerator.EliteGenerateMonsterArmor();
                // eliteMonstersGenerator.EliteGenerateMonsterDmg();
                // eliteMonstersGenerator.EliteGenerateMonsterHp();
                // bosses.GenerateBoss();
                // locationGenerator.GenerateLocation();
                // ItemGenerator.itemGeneratorAsync(characterId: "5aa4eeb4482907ab7479adf9", level: 1, quantity: 30);
                // BotsGenerator.BotsGeneratorAsync();
            }
            catch (Exception e) 
            {
                Console.WriteLine(e.ToString());
            }
            Stop();
            using (var registration = cts.Token.Register(listener.Close))
            {
                while (!cts.IsCancellationRequested)
                {
                    Console.WriteLine("Waiting for a connection...");
                    try
                    {
                        var client = await AcceptClient(cts.Token);
                        var task = Task.Run(() => ClientService(client, cts.Token));
                    }
                    catch (OperationCanceledException) { break; }
                    catch (ObjectDisposedException) when (cts.Token.IsCancellationRequested) { break; }
                }
            }
        }
 
        private static async Task ClientService(Client client, CancellationToken ct)
        {
            await ReadAsync(client, ct);
        }
 
        private static async Task<Client> AcceptClient(CancellationToken ct)
        {
            var socket = await Task<Socket>.Factory.FromAsync(listener.BeginAccept, listener.EndAccept, null).ConfigureAwait(false);
            ct.ThrowIfCancellationRequested();
            var client = new Client();
            client.socket = socket;
            client.id = clients.Count;
            client.ip = socket.RemoteEndPoint.ToString();
            clients.Add(client);
            Console.WriteLine("Clients count is: " + clients.Count + " IP: " + client.ip);
            return client;
        }
 
        private static async void Stop()
        {
            await Task.Delay(7000);
            try
            {
                cts.Cancel();
            }
            finally
            {
            }
            for (int i = 0; i < clients.Count; i++)
            {
                clients[i].socket.Shutdown(SocketShutdown.Both);
            }
            
            Console.Write("Server Stopped! ");
        }
 
        private static async Task ReadAsync(Client client, CancellationToken ct)
        {
            while (!ct.IsCancellationRequested)
            {
                try
                {
                    client.socket.SetKeepAlive(1000, 1000);
                    string content = string.Empty;
 
                    var bytesRead = await Task.Factory.FromAsync(
                        client.socket.BeginReceive(client.buffer, 0, Client.BufferSize, SocketFlags.None, null, client.socket),
                            client.socket.EndReceive).ConfigureAwait(false);
                    ct.ThrowIfCancellationRequested();
 
                    if (bytesRead > 0) 
                    {
                        client.sb.Append(Encoding.UTF8.GetString(
                            client.buffer, 0, bytesRead));
                        content = client.sb.ToString();
 
                        //Check for end-of-file tag. If it is not there, read 
                        //more data.
                        if (content.EndsWith("±☭")) 
                        {
                            string[] fullCommands = content.Remove(content.Length - 1).Split('☭');
 
                            for (int i = 0; i < fullCommands.Length; i++)
                            {
                                string[] commands = fullCommands[i].Remove(fullCommands[i].Length - 1).Split('§');
                            
                                //string[] commands = content.Remove(content.Length - 2).Split('§');
                                //All the data has been read from the 
                                //client. Display it on the console.
                                Console.WriteLine("Read {0} bytes from socket. \nData : {1}",
                                    content.Length, content );
                                //для дебага
                                for (int j = 0; j < commands.Length; j++)
                                {
                                    Console.WriteLine($"Command {j}: " + commands[j]);
                                }
 
                                if (commands[0].Contains("GetServers"))
                                    CommandProcessor.GetServers(commands[0], client.socket);
                                else if (commands[0].Contains("Login"))
                                    CommandProcessor.Login(commands[0], commands[1], client.socket);
                                else if (commands[0].Contains("GetCharacterClasses"))
                                    CommandProcessor.GetCharacterClasses(commands[0], commands[1], client.socket);
                                else if (commands[0].Contains("CreateCharacter"))
                                    CommandProcessor.CreateCharacter(commands[0], commands[1], client.socket);
                                else if (!string.IsNullOrEmpty(commands[2]) && !string.IsNullOrEmpty(commands[3]))
                                {
                                    if(clients.Any(c => c.authToken == commands[2] && c.characterId == commands[3]))
                                    {
                                        CommandProcessor.CommandHandler(commands[0], commands[1], client.socket);
                                    }
                                    else
                                    {
                                        SocketClose(client);
                                        break;
                                    }
                                }
                                else
                                {
                                    SocketClose(client);
                                    break;
                                }
                                    
                                client.sb.Clear();
                                Array.Clear(commands, 0, commands.Length); //очищаем команды
                            }
                        }
                    }
                    else
                    {
                        //Disconnect clients
                        SocketClose(client);
                        break;
                    }
                }
                catch (SocketException se)
                {
                    Console.WriteLine(se.SocketErrorCode.ToString());
                    SocketClose(client);
                    break;
                }
            }
        }
 
        private static void SocketClose(Client client)
        {
            client.socket.Shutdown(SocketShutdown.Both);
            client.socket.Disconnect(false);
            client.socket.Close();
 
            if(!string.IsNullOrEmpty(client.characterId))
                ClientsHandler.UpdateUserConnectDisconnectTime(client.characterId, "lastSeen").GetAwaiter().GetResult();
 
            clients.Remove(client);
            Console.WriteLine($"User with ID: {client.id} and IP: {client.ip} is disconnected");
        }
 
        public static async Task SendAsync(Socket client, string data)
        {
            // Convert the string data to byte data using UTF8 encoding.
            byte[] byteData = Encoding.UTF8.GetBytes(data);
            try
            {
                var bytesSent = await Task.Factory.FromAsync(
                    client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, null, client),
                        client.EndSend).ConfigureAwait(false);
                Console.WriteLine("Sent {0} bytes to client with ID: {1}.", bytesSent, clients.FirstOrDefault(c => c.socket == client).id);
            }
            catch(SocketException e)
            {
                Console.WriteLine("Send: " + e.SocketErrorCode);
            }         
        }
 
        public static async Task SendToAllClientsAsync(string data)
        {
            for (int i = 0; i < clients.Count; i++)
            {
                await SendAsync(clients[i].socket, data);
            }
        }
        
        public static int Main(String[] args) 
        {
            StartListening().GetAwaiter().GetResult();
          
            return 0;
        }
    }
}
Добавлено через 46 минут
я тут потестил и получается если использовать один токен отмены то данной записи достаточно чтоб отключить сервер без проблем
Цитата Сообщение от kolorotur Посмотреть сообщение
var token = cts.Token;
using (var registration = token.Register(client.Close))
{
* *while (!token.IsCancellationRequested)
* *{
* * * Console.WriteLine("Waiting for a connection...");
* * * try
* * * {
* * * * *var client = await AcceptClient();
* * * * *var task = ClientService(client, token);
* * * }
* * * catch (OperationCancelledException) { break; }
* * * catch (ObjectDisposedException) when (token.IsCancellationRequested) { break; }
* *}
}
Цитата Сообщение от kolorotur Посмотреть сообщение
ct.ThrowIfCancellationRequested();
и запись не на что не влияет ))
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
10.04.2018, 19:11
Цитата Сообщение от AlexeyTsoy Посмотреть сообщение
я тут потестил
Плохо потестили.
Многопоточные приложения вообще не сильно поддаются тестированию.
Например, если будет такая очередность инструкций:
1. Вызывается Cancel
2. Заканчивается вызов await
3. listener закрывается
4. Метод AcceptClient продолжает работать и завершает выполнение.
5. Запускается процесс считывания.

По-хорошему вызов ThrowIfCancellationRequested должен быть перед return, но у вас ранее идет добавление в список, потому я добавил его сразу после завершения асинхронного метода.
0
0 / 0 / 0
Регистрация: 24.01.2018
Сообщений: 22
10.04.2018, 19:29  [ТС]
а ок понял))Буду рад любым замечаниям
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
10.04.2018, 19:29
Помогаю со студенческими работами здесь

Вызов методов классов друг у друга (вызов метода из другого класса)
Есть несколько классов, которые могут вызывать методы (функции) друг у друга. Логика: класс1 имеет метод А, класс2 имеет метод Б, класс1...

Task не останавливается
Всем доброго времени суток! Есть такой код(не обращайте внимание на частички кода из WPF, это не имеет какое - либо значение): ...

Отмена Task
Здраствуйте, Таск запускается при нажатии Calculate, проводит долгую операцию с двумя числами и потом выводит результат, UI при этом...

Отмена Task
Добрый день, у меня очередной вопрос к Гуру..) Есть Таск, который можно отменить извне с помощью CancellationToken. Таском...

Работа с Task
Здравствуйте. Как проверить, завершилась ли задача запущенная с Task? Если не завершилась, то как её прервать?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru