Truyền thông hướng sự kiện trong Game Engine: Có hay không?


62

Tôi đang đọc Toàn bộ mã hóa trò chơi và tác giả khuyến nghị giao tiếp theo hướng sự kiện giữa các đối tượng và mô-đun trò chơi.

Về cơ bản, tất cả các diễn viên trò chơi sống nên giao tiếp với các mô-đun chính (Vật lý, AI, Logic trò chơi, Chế độ xem trò chơi, v.v.) thông qua hệ thống nhắn tin sự kiện nội bộ. Điều này có nghĩa là phải thiết kế một người quản lý sự kiện hiệu quả. Một hệ thống được thiết kế tồi sẽ ăn chu kỳ CPU, đặc biệt ảnh hưởng đến nền tảng di động.

Đây có phải là một cách tiếp cận đã được chứng minh và đề nghị? Làm thế nào tôi nên quyết định có nên sử dụng nó?


Xin chào James, cảm ơn bạn đã trả lời. Tôi thích nó, mặc dù cuốn sách mô tả giao tiếp hướng sự kiện là hệ thống thực sự phức tạp dường như tiêu tốn rất nhiều tài nguyên CPU.
Bunkai.Satori

Đặt nó vào một câu trả lời và được thêm vào trong một liên kết đến tổng quan cấp cao của một hệ thống nhắn tin sự kiện mà tôi đã đưa ra như một câu trả lời về lỗi tràn ngăn xếp. Thưởng thức! Chỉ cần nhớ rằng những gì một số người cho là phức tạp không cần phải có :)
James

Bạn có thể kiểm tra câu trả lời này cũng được thực hiện bằng c ++ 11: stackoverflow.com/a/22703242/2929762
user2826084

libuvgần đây đã nổi lên như một thư viện sự kiện thành công. (Nó ở C. Node.js là trường hợp sử dụng nổi tiếng nhất của nó.)
Anko

Câu trả lời:


46

Đây là một bản mở rộng nhận xét của tôi để trả lời đầy đủ, như được đề xuất.

Vâng , đơn giản và đơn giản. Giao tiếp cần phải xảy ra và trong khi có những tình huống 'Chúng ta đã ở đó chưa?' bỏ phiếu -type là bắt buộc, có những thứ kiểm tra xem liệu họ có nên làm việc gì khác thường làm lãng phí thời gian không. Thay vào đó, bạn có thể khiến họ phản ứng với những việc họ được bảo phải làm. Thêm vào đó, một lộ trình giao tiếp được xác định rõ giữa các đối tượng / hệ thống / mô-đun giúp tăng đáng kể các thiết lập song song.

Tôi đã cung cấp tổng quan cấp cao về hệ thống nhắn tin sự kiện trên Stack Overflow . Tôi đã sử dụng nó từ trường học vào các tựa game chuyên nghiệp và bây giờ là các sản phẩm không chơi game, phù hợp với trường hợp sử dụng mỗi lần tất nhiên.

EDIT : Để giải quyết một câu hỏi bình luận về cách bạn biết đối tượng nào sẽ nhận được thông báo : Bản thân các đối tượng cần Requestđược thông báo về các sự kiện. Của bạn EventMessagingSystem(EMS) sẽ cần một Register(int iEventId, IEventMessagingSystem * pOjbect, (EMSCallback)fpMethodToUseForACallback)cũng như phù hợp Unregister(làm cho một mục duy nhất cho một iEventIdra khỏi con trỏ đối tượng và gọi lại). Bằng cách này, khi một đối tượng muốn biết về một thông điệp có thể Register()với hệ thống. Khi nó không còn cần biết về các sự kiện, nó có thểUnregister(). Rõ ràng, bạn muốn có một nhóm các đối tượng đăng ký gọi lại này và một cách hiệu quả để thêm / xóa chúng khỏi danh sách. (Tôi thường sử dụng các mảng tự đặt hàng; một cách thú vị để nói rằng họ theo dõi phân bổ của riêng mình giữa một nhóm các đối tượng không sử dụng và các mảng thay đổi kích thước của chúng tại chỗ khi cần).

EDIT : Đối với một trò chơi hoàn toàn hoạt động với vòng lặp cập nhật và hệ thống nhắn tin sự kiện, bạn có thể muốn kiểm tra một dự án trường học cũ của tôi . Bài viết Stack Overflow được liên kết ở trên cũng đề cập đến nó.


Xin chào James, vì tôi đang nghĩ về hệ thống nhắn tin sự kiện nội bộ nhiều hơn, tôi nghĩ rằng nó không cần thêm chi phí cho công cụ. Ví dụ: nếu có 50 đối tượng trên màn hình, nhưng chỉ có 5 đối tượng được cho là thay đổi hành vi của họ; theo hệ thống truyền thống, tất cả 50 obejcts sẽ cần kiểm tra tất cả các hành động có thể của họ, để xem liệu họ có nên làm gì không. Tuy nhiên, với việc sử dụng tin nhắn sự kiện, chỉ có 5 tin nhắn sẽ được gửi đến 5 phản đối đó với sự thay đổi hành động cụ thể. Trông giống như một cách tiếp cận tiết kiệm thời gian.
Bunkai.Satori

Cách hệ thống được liên kết với câu trả lời trên của tôi hoạt động là các đối tượng chỉ đăng ký để nghe về các tin nhắn họ muốn .. Chúng tôi sẽ sử dụng điều này cho lợi thế của chúng tôi trong các trò chơi video có thể có 100-200 đối tượng ở cấp độ, nhưng bạn chỉ 'kích hoạt' những thứ mà người chơi có thể tương tác trực tiếp, giữ cho số lượng thứ nghe xuống khoảng 10 hoặc hơn. Tất cả trong tất cả các loại hệ thống này nên 'thông minh hơn' một 'chúng ta đã ở đó chưa?' hệ thống bỏ phiếu, và nên giảm chi phí hoạt động của động cơ, ít nhất là liên quan đến truyền thông.
James

Có một điều liên quan đến hệ thống điều khiển sự kiện làm tôi bối rối: Trong hệ thống truyền thống, nơi mọi đối tượng đều có bộ phương thức được xác định trước (OnUpdate (), OnMove (), OnAI (), OnCollisionCheck () ...) thường được gọi, mọi đối tượng đều được gọi chịu trách nhiệm kiểm tra và quản lý trạng thái của nó. Trong hệ thống điều khiển sự kiện, phải có một số điều kiện kiểm tra hệ thống chính của mọi đối tượng và sau đó gửi tin nhắn đến những nơi phát hiện sự kiện nào đó. Đối với tôi, điều này chuẩn hóa các trạng thái đối tượng có thể và giới hạn tự do tạo ra hành vi đối tượng duy nhất. Điều đó có đúng không?
Bunkai.Satori

Mẫu Observer là sự thay thế điển hình cho việc bỏ phiếu. vi.wikipedia.org/wiki/Observer_potype
Ricket

1
@ hamlin11 Vui mừng thông tin này vẫn giúp ích cho mọi người :) Liên quan đến việc đăng ký, nếu điều này xảy ra khá nhiều, hãy nhớ rằng bạn sẽ muốn tối ưu hóa nó cho tốc độ, có một nhóm các đối tượng đăng ký để rút ra, v.v.
James

22

Ý kiến ​​của tôi là bạn nên bắt đầu thực hiện trò chơi của mình và thực hiện những gì bạn thấy thoải mái. Khi bạn làm điều đó, bạn sẽ thấy mình sử dụng MVC ở một số nơi, nhưng không phải ở những nơi khác; sự kiện một số nơi, nhưng không phải những nơi khác; thành phần một số nơi, nhưng thừa kế những người khác; thiết kế sạch sẽ một số nơi, và thiết kế tồi tàn khác.

Và điều đó không sao, bởi vì khi bạn hoàn thành, bạn thực sự sẽ có một trò chơi, và một trò chơi tuyệt vời hơn một hệ thống truyền tin nhắn.


2
Xin chào Joe, cảm ơn câu trả lời của bạn, tôi thích nó. Bạn tin rằng, không cần phải đẩy bất kỳ sự giả mạo nào một cách giả tạo. Tôi nên thiết kế các ứng dụng như tôi nghĩ sẽ hoạt động và nếu một cái gì đó không hoạt động sau này, tôi sẽ chỉ làm lại nó.
Bunkai.Satori

Đây là lý do tại sao hệ thống tồn tại. Nhiều người đã làm đúng như những gì bạn đã nói, chứng kiến ​​cơn ác mộng và nói rằng phải có một cách tốt hơn. Bạn có thể lặp lại sai lầm của mình hoặc học hỏi từ chúng và có thể tiến bộ hơn.
dùng441521

16

Một câu hỏi tốt hơn là, có những lựa chọn thay thế nào? Trong một hệ thống phức tạp như vậy với các mô-đun được phân chia hợp lý cho vật lý, AI, v.v., làm thế nào khác bạn có thể bố trí các hệ thống này?

Truyền tin nhắn dường như là giải pháp "tốt nhất" cho vấn đề này. Tôi không thể nghĩ ra các lựa chọn thay thế ngay bây giờ. Nhưng có rất nhiều ví dụ về thông điệp truyền qua trong thực tế. Trong thực tế, các hệ điều hành sử dụng thông điệp truyền cho một số chức năng của chúng. Từ Wikipedia :

Thông điệp cũng thường được sử dụng theo nghĩa tương tự như một phương tiện giao tiếp giữa các quá trình; kỹ thuật phổ biến khác là luồng hoặc đường ống, trong đó dữ liệu được gửi dưới dạng một chuỗi các mục dữ liệu cơ bản thay thế (phiên bản cấp cao hơn của mạch ảo).

(Giao tiếp giữa các quá trình là giao tiếp giữa các quy trình (tức là chạy các chương trình) trong môi trường hệ điều hành)

Vì vậy, nếu nó đủ tốt cho một hệ điều hành, nó có thể đủ tốt cho một trò chơi, phải không? Cũng có những lợi ích khác, nhưng tôi sẽ để bài viết Wikipedia về tin nhắn đi qua giải thích.

Tôi cũng đã hỏi một câu hỏi về Stack Overflow, "Cấu trúc dữ liệu cho thông điệp truyền trong một chương trình?" , mà bạn có thể muốn đọc qua.


Xin chào Ricket, cảm ơn câu trả lời của bạn. Bạn hỏi những cách khác có thể được sử dụng để cho các đối tượng trò chơi giao tiếp với nhau. Điều gì đến với tâm trí của tôi là các phương thức gọi trực tiếp. Thật là tuyệt vời, nó không mang lại cho bạn sự linh hoạt cao như vậy, nhưng mặt khác, nó tránh việc tạo thông điệp sự kiện, truyền, đọc, v.v. Chúng ta vẫn nói về nền tảng di động, không phải về các trò chơi cao cấp. Hệ điều hành havily sử dụng hệ thống nhắn tin nội bộ. Tuy nhiên, nó không được thiết kế để chạy trong thời gian thực, nơi mà ngay cả sự chậm trễ nhỏ cũng gây ra sự gián đoạn.
Bunkai.Satori

Hãy để tôi giữ câu hỏi này mở trong một thời gian. Tôi muốn thu thập thêm ý kiến, trước khi đóng nó.
Bunkai.Satori

Các cuộc gọi phương thức trực tiếp thường dẫn đến việc ghép các lớp gọi nhau. Điều này không tốt cho một hệ thống được tách thành các thành phần được cho là có thể hoán đổi cho nhau. Có một số mẫu có thể tạo điều kiện cho các cuộc gọi phương thức trực tiếp với ít sự gắn kết hơn (ví dụ: phần "bộ điều khiển" của MVC về cơ bản phục vụ mục đích này để tạo thuận lợi cho các truy vấn của mô hình, trừ khi bạn ghép nối chúng), nhưng nói chung, truyền tin nhắn là cách duy nhất cho một hệ thống có khớp nối bằng không giữa các hệ thống con (hoặc ít nhất là cách duy nhất tôi biết).
Ricket

Ah, vậy bây giờ chúng tôi bắt đầu thảo luận về các mẫu. Tôi đã nghe một chút về phương pháp lập trình này. Bây giờ, tôi bắt đầu hiểu những mẫu nào. Đó là cái nhìn hoàn toàn khác nhau về thiết kế ứng dụng. Bạn kiểm soát các đối tượng, trong khi sử dụng cùng một mã để kiểm tra mọi đối tượng. Sau khi kiểm tra đối tượng bạn đặt thuộc tính của nó cho phù hợp.
Bunkai.Satori

1
Câu hỏi "Cấu trúc dữ liệu ..." có câu trả lời TUYỆT VỜI. Tôi muốn nói, câu trả lời được lựa chọn của bạn và bài viết Nebula3 đi kèm là mô tả tốt nhất về kiến ​​trúc công cụ trò chơi có kích thước của chúng tôi từng thấy.
deft_code

15

Tin nhắn thường hoạt động tốt khi:

  1. Điều gửi tin nhắn không quan tâm nếu nó nhận được.
  2. Người gửi không cần nhận được phản hồi ngay lập tức từ người nhận.
  3. Có thể có nhiều người nhận lắng nghe một người gửi.
  4. Tin nhắn sẽ được gửi không thường xuyên hoặc không thể đoán trước. (Nói cách khác, nếu mọi đối tượng cần nhận thông báo "cập nhật" từng khung hình, tin nhắn sẽ không thực sự mua cho bạn nhiều.)

Nhu cầu của bạn càng phù hợp với danh sách đó, thông điệp phù hợp sẽ càng tốt. Ở cấp độ rất chung, chúng khá tốt. Họ làm tốt công việc không lãng phí chu kỳ CPU chỉ để không làm gì khác với bỏ phiếu hoặc các giải pháp toàn cầu khác. Chúng rất tuyệt vời trong việc tách các phần của một cơ sở mã, điều này luôn hữu ích.


4

Câu trả lời tuyệt vời cho đến nay từ James và Ricket, tôi chỉ trả lời để thêm một lưu ý thận trọng. Truyền thông / truyền thông hướng sự kiện chắc chắn là một công cụ quan trọng trong kho vũ khí của nhà phát triển, nhưng nó có thể dễ dàng bị lạm dụng. Khi bạn có một cái búa, mọi thứ trông giống như một cái đinh, vân vân.

Bạn hoàn toàn, 100%, nên suy nghĩ về luồng dữ liệu xung quanh tiêu đề của bạn và nhận thức đầy đủ về cách thông tin được truyền từ hệ thống con này sang hệ thống con khác. Trong một số trường hợp, tin nhắn đi qua rõ ràng là tốt nhất; ở những người khác, có thể thích hợp hơn cho một hệ thống con hoạt động trên một danh sách các đối tượng được chia sẻ, cho phép các hệ thống con khác cũng hoạt động trên cùng một danh sách được chia sẻ; và nhiều phương pháp khác bên cạnh.

Tại mọi thời điểm, bạn nên biết hệ thống con nào cần truy cập vào dữ liệu nào và khi nào chúng cần truy cập vào dữ liệu đó. Điều này ảnh hưởng đến khả năng song song và tối ưu hóa của bạn, cũng như giúp bạn tránh được các vấn đề quỷ quyệt hơn khi các bộ phận khác nhau trong động cơ của bạn trở nên quá chặt chẽ. Bạn cần suy nghĩ về việc giảm thiểu việc sao chép dữ liệu không cần thiết xung quanh dữ liệu và cách bố trí dữ liệu của bạn ảnh hưởng đến việc sử dụng bộ đệm của bạn. Tất cả sẽ hướng dẫn bạn giải pháp tốt nhất trong từng trường hợp.

Điều đó đang được nói, gần như mọi trò chơi tôi từng làm việc đều có một hệ thống thông báo sự kiện / thông báo không đồng bộ vững chắc. Nó cho phép bạn viết mã ngắn gọn, hiệu quả có thể bảo trì, ngay cả khi đối mặt với nhu cầu thiết kế phức tạp và nhanh chóng thay đổi.


+1 để biết thêm thông tin tốt. Xin chào, MrCranky, Cảm ơn. Theo những gì bạn nói, có lẽ cũng đáng để xem xét hệ thống Nhắn tin Sự kiện ngay cả đối với các trò chơi di động. Tuy nhiên, việc lập kế hoạch không nên bị bỏ qua, và nó cần được xem xét rất kỹ nơi sử dụng hệ thống nhắn tin.
Bunkai.Satori

4

Chà, tôi biết rằng bài đăng này khá cũ, nhưng tôi không thể cưỡng lại.

Gần đây tôi đã xây dựng một công cụ trò chơi. Nó sử dụng các thư viện bên 3d để kết xuất và vật lý, nhưng tôi đã viết phần cốt lõi, định nghĩa và xử lý các thực thể và logic trò chơi.

Động cơ chắc chắn theo một cách tiếp cận truyền thống. Có một vòng cập nhật chính gọi chức năng cập nhật cho tất cả các thực thể. Va chạm được báo cáo trực tiếp bằng cách gọi lại trên các thực thể. Giao tiếp giữa các thực thể được thực hiện bằng cách sử dụng con trỏ thông minh trao đổi giữa các thực thể.

Có một hệ thống tin nhắn nguyên thủy, Điều đó chỉ xử lý một nhóm nhỏ thực thể cho các thông điệp động cơ. Những thông báo đó được ưu tiên xử lý khi kết thúc tương tác trò chơi (ví dụ là tạo hoặc hủy thực thể) vì chúng có thể gây rối với danh sách cập nhật. Vì vậy, vào cuối mỗi vòng lặp trò chơi, một danh sách nhỏ các tin nhắn được tiêu thụ.

Mặc dù hệ thống thông điệp nguyên thủy, tôi sẽ nói rằng hệ thống này chủ yếu là "cập nhật dựa trên vòng lặp".

Tốt. Sau khi sử dụng hệ thống này, tôi nghĩ rằng nó rất đơn giản, nhanh chóng và được tổ chức tốt. Logic trò chơi có thể nhìn thấy và khép kín bên trong các thực thể, không năng động như một thông điệp. Tôi thực sự sẽ không làm cho nó bị điều khiển bởi vì theo ý kiến ​​của tôi, các hệ thống sự kiện đưa ra sự phức tạp không cần thiết cho logic trò chơi và làm cho mã trò chơi trở nên rất khó hiểu và gỡ lỗi.

Nhưng, tôi cũng nghĩ rằng một hệ thống "cập nhật dựa trên vòng lặp" thuần túy như của tôi cũng có một số vấn đề.

Ví dụ: Trong một số thời điểm, một thực thể có thể ở trạng thái "không làm gì cả", có thể đang chờ người chơi tiếp cận hoặc một cái gì đó khác. Trong hầu hết các trường hợp đó, thực thể đốt cháy thời gian của bộ xử lý mà không có gì và tốt hơn là tắt thực thể đó và bật nó khi một sự kiện nào đó xảy ra.

Vì vậy, trong công cụ trò chơi tiếp theo của tôi, tôi sẽ áp dụng một cách tiếp cận khác. Các thực thể sẽ tự đăng ký cho các hoạt động của động cơ, như cập nhật, vẽ, phát hiện va chạm và như vậy. Mỗi sự kiện này sẽ có danh sách các giao diện thực thể riêng biệt cho các thực thể thực tế.


Bạn sẽ thấy rằng các thực thể trở thành các cơ sở mã quái vật trở nên khó quản lý với bất kỳ sự phức tạp nào trong trò chơi. Cuối cùng, bạn sẽ ghép chúng lại với nhau và nó sẽ hút để thêm hoặc thay đổi các tính năng. Bằng cách chia nhỏ các tính năng của bạn thành các thành phần nhỏ, việc bảo trì và thêm các tính năng sẽ dễ dàng hơn. Sau đó, câu hỏi trở thành làm thế nào để các thành phần này giao tiếp? Câu trả lời là các sự kiện từ một chức năng nâng cao thành phần của một thành phần khác trong khi truyền dữ liệu nguyên thủy cùng với các đối số.
dùng441521

3

Đúng. Đó là một cách rất hiệu quả để các hệ thống trò chơi giao tiếp với nhau. Các sự kiện giúp bạn tách rời nhiều hệ thống và thậm chí có thể biên dịch mọi thứ một cách riêng biệt mà không cần biết đến sự tồn tại của nhau. Điều này có nghĩa là các lớp của bạn có thể được tạo mẫu dễ dàng hơn và thời gian biên dịch nhanh hơn. Quan trọng hơn, bạn kết thúc với một thiết kế mã phẳng thay vì một mớ hỗn độn phụ thuộc.

Một lợi ích lớn khác của các sự kiện là chúng dễ dàng được truyền phát qua mạng hoặc kênh văn bản khác. Bạn có thể ghi lại chúng để phát lại sau. Các khả năng là vô tận.

Một lợi ích khác: Bạn có thể có một số hệ thống con lắng nghe cho cùng một sự kiện. Ví dụ: tất cả các chế độ xem từ xa (người chơi) của bạn có thể tự động đăng ký vào sự kiện tạo thực thể và sinh ra các thực thể trên mỗi máy khách với rất ít công việc từ phía bạn. Hãy tưởng tượng bạn sẽ làm việc nhiều hơn thế nào nếu bạn không sử dụng các sự kiện: bạn sẽ phải thực hiện Update()các cuộc gọi ở đâu đó hoặc có thể gọi view->CreateEntitytừ logic Trò chơi (nơi kiến ​​thức về chế độ xem và thông tin mà nó yêu cầu không thuộc về). Thật khó để giải quyết vấn đề đó mà không có sự kiện.

Với các sự kiện, bạn có được một giải pháp thanh lịch và tách rời, hỗ trợ vô số đối tượng các loại không giới hạn, tất cả chỉ có thể đăng ký vào các sự kiện và thực hiện công việc của chúng khi có điều gì đó xảy ra trong trò chơi của bạn. Đó là lý do tại sao các sự kiện là tuyệt vời.

Thêm chi tiết và thực hiện ở đây .


Điểm tuyệt vời, mặc dù một chiều. Tôi luôn nghi ngờ về tính tổng quát: Có trường hợp chống sử dụng cho các sự kiện không?
Anko

1
Điểm chống sử dụng: khi bạn hoàn toàn phải có phản ứng trực tiếp. Khi bất cứ điều gì bạn muốn làm trong một số sự kiện luôn được thực hiện trực tiếp và trong cùng một chuỗi. Khi hoàn toàn không có sự chậm trễ bên ngoài nào có thể trì hoãn việc hoàn thành cuộc gọi. Khi bạn chỉ có phụ thuộc tuyến tính. Khi bạn hiếm khi làm nhiều việc cùng một lúc. Nếu bạn nhìn vào node.js, tất cả các cuộc gọi io đều dựa trên sự kiện. Node.js là một nơi mà các sự kiện đã được triển khai chính xác 100%.
dùng2826084

Hệ thống sự kiện không cần phải đồng bộ. Bạn có thể sử dụng coroutines để mô phỏng async, đồng thời nhận được đồng bộ hóa khi bạn cần.
dùng441521

2

Từ những gì tôi đã thấy, dường như không phổ biến lắm khi có một công cụ hoàn toàn dựa trên tin nhắn. Tất nhiên, có những hệ thống con cho vay rất tốt cho một hệ thống nhắn tin như mạng, GUI và có thể cả những hệ thống khác. Nói chung, có một vài vấn đề tôi có thể nghĩ đến:

  • Công cụ trò chơi đối phó với số lượng lớn dữ liệu.
  • Các trò chơi phải nhanh (20-30 FPS nên là tối thiểu).
  • Thường thì cần phải biết nếu một cái gì đó đã được thực hiện hoặc khi nào nó sẽ được thực hiện.

Với cách tiếp cận phổ biến của nhà phát triển trò chơi "nó phải hiệu quả nhất có thể", hệ thống nhắn tin như vậy không phải là phổ biến.

Tuy nhiên, tôi đồng ý rằng bạn chỉ nên tiếp tục và thử nó. Chắc chắn có rất nhiều lợi thế với một hệ thống như vậy và sức mạnh tính toán có sẵn ngày nay.


Tôi không biết nếu tôi đồng ý với điều này. Thay thế là bỏ phiếu đó là lãng phí. Chỉ đáp ứng với các sự kiện là hiệu quả nhất. Vâng, vẫn sẽ có một số cuộc bỏ phiếu sẽ xảy ra và gây ra các sự kiện nhưng cũng có một số điều tốt được định hướng sự kiện.
dùng441521

Tôi cũng không đồng ý với điều này. Số lượng công cụ trò chơi dữ liệu xử lý phần lớn không liên quan, vì tin nhắn sự kiện được thiết kế để liên lạc với hệ thống con. Các đối tượng trò chơi không nên giao tiếp trực tiếp với các đối tượng trò chơi khác (mặc dù các trình xử lý sự kiện tùy chỉnh trong các đối tượng có thể là một ngoại lệ). Do số lượng hệ thống con tương đối ít này và các lĩnh vực "quan tâm" của chúng, chi phí hiệu năng của một đường trục nhắn tin được thiết kế tốt, trong một công cụ đa luồng là không đáng kể.
Ian Young
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.