Làm thế nào để giữ cấu trúc dữ liệu được đồng bộ hóa qua mạng?


12

Bối cảnh

Trong trò chơi tôi đang làm việc (một cuộc phiêu lưu đồ họa điểm và nhấp chuột), hầu hết mọi thứ xảy ra trong thế giới trò chơi đều được điều khiển bởi một người quản lý hành động có cấu trúc hơi giống:

nhập mô tả hình ảnh ở đây

Vì vậy, ví dụ nếu kết quả kiểm tra một đối tượng sẽ khiến nhân vật nói xin chào, đi bộ một chút rồi ngồi xuống, tôi chỉ cần sinh ra đoạn mã sau:

var actionGroup = actionManager.CreateGroup();
actionGroup.Add(new TalkAction("Guybrush", "Hello there!");
actionGroup.Add(new WalkAction("Guybrush", new Vector2(300, 300));
actionGroup.Add(new SetAnimationAction("Guybrush", "Sit"));

Điều này tạo ra một nhóm hành động mới (toàn bộ một dòng trong hình trên) và thêm nó vào trình quản lý. Tất cả các nhóm được thực hiện song song, nhưng các hành động trong mỗi nhóm được nối lại với nhau để nhóm thứ hai chỉ bắt đầu sau khi nhóm thứ nhất kết thúc. Khi hành động cuối cùng trong một nhóm kết thúc, nhóm bị phá hủy.

Vấn đề

Bây giờ tôi cần sao chép thông tin này trên một mạng, để trong một phiên nhiều người chơi, tất cả người chơi đều thấy điều tương tự. Việc nối tiếp các hành động riêng lẻ không phải là vấn đề. Nhưng tôi là người mới bắt đầu tuyệt đối khi kết nối mạng và tôi có một vài câu hỏi. Tôi nghĩ vì mục đích đơn giản trong cuộc thảo luận này, chúng ta có thể trừu tượng hóa thành phần quản lý hành động thành đơn giản:

var actionManager = new List<List<string>>();

Tôi nên tiến hành như thế nào để giữ cho nội dung của cấu trúc dữ liệu trên được đồng bộ hóa giữa tất cả người chơi?

Bên cạnh câu hỏi cốt lõi, tôi cũng có một vài mối quan tâm khác liên quan đến nó (tức là tất cả các hàm ý có thể có của cùng một vấn đề ở trên):

  • Nếu tôi sử dụng kiến ​​trúc máy chủ / máy khách (với một trong những người chơi đóng vai trò là máy chủ và máy khách) và một trong những khách hàng đã sinh ra một nhóm hành động, anh ta nên thêm chúng trực tiếp vào người quản lý hoặc chỉ gửi yêu cầu đến máy chủ, đến lượt nó sẽ ra lệnh cho mọi khách hàng thêm nhóm đó ?
  • Những gì về mất gói và tương tự? Trò chơi mang tính quyết định, nhưng tôi nghĩ rằng bất kỳ sự khác biệt nào trong chuỗi hành động được thực hiện trong máy khách đều có thể dẫn đến các trạng thái không nhất quán trên thế giới. Làm thế nào để tôi bảo vệ chống lại loại vấn đề đó ?
  • Điều gì xảy ra nếu tôi thêm quá nhiều hành động cùng một lúc, sẽ không gây ra sự cố cho kết nối? Có cách nào để giảm bớt điều đó?

5
Bắt buộc "đọc liên kết đầu tiên" này gafferongames.com/networking-for-game-programmer không kỹ thuật mặc dù có một số mã nhưng nó dài dòng và giải thích các tùy chọn của bạn khá tốt. Thêm vào đó, nó sẽ giúp bạn bình đẳng với những người khác đọc liên kết đó và đó là một nơi hạnh phúc.
Patrick Hughes

@PatrickHughes Cảm ơn đã liên kết! Tôi chắc chắn sẽ đọc tất cả.
David Gouveia

Có một số gói quản lý loại điều này cho bạn. Tôi không biết nó hiệu quả hay nhanh như thế nào, nhưng NowJS ( nowjs.com ) là một ví dụ thú vị. Từ quan điểm của các nhà phát triển, bạn chỉ cần chia sẻ cấu trúc dữ liệu giữa máy khách và máy chủ. Thay đổi một, thay đổi khác.
Tim Holt

Câu trả lời:


9

Nếu tôi sử dụng kiến ​​trúc máy chủ / máy khách (với một trong những người chơi đóng vai trò là máy chủ và máy khách) và một trong số các máy khách đã sinh ra một nhóm hành động, anh ta nên thêm chúng trực tiếp vào người quản lý hoặc chỉ gửi yêu cầu đến máy chủ, đến lượt nó sẽ ra lệnh cho mọi khách hàng thêm nhóm đó?

Bạn có ba lựa chọn trong trường hợp này, hai trong số đó bạn đã đề cập. Sự lựa chọn của lựa chọn nào bạn thực hiện phụ thuộc vào nhiều yếu tố mà chỉ bạn mới có thể là người đánh giá tốt nhất về:

1: Khách hàng sinh ra nhóm hành động thêm chúng trực tiếp và sau đó gửi chúng đến máy chủ. Máy chủ chấp nhận nó mà không cần kiểm tra

* Lợi ích của phương pháp này là khi có liên quan đến khách hàng, hành động của chính họ mang lại cho họ phản hồi tức thì không phụ thuộc vào độ trễ của mạng. Nhược điểm là các tin nhắn của khách hàng được máy chủ chấp nhận là sự thật (có thể không phải vậy) *

2: Máy khách gửi yêu cầu đến máy chủ, từ đó phát ra yêu cầu cho mọi người, bao gồm cả máy khách đã tạo ra yêu cầu.

Lợi ích của phương pháp này là máy chủ hiện đang kiểm soát mọi thứ xảy ra trong trò chơi, giúp giảm bớt hầu hết các vấn đề với hoạt động bất hợp pháp (ở đây bất hợp pháp có thể từ khách hàng cắt tường để thực hiện di chuyển hiện là bất hợp pháp vì máy chủ có thêm thông tin mà khách hàng không có khi khách hàng thực hiện một động thái cụ thể)

3: Máy khách sinh ra nhóm hành động, thêm chúng trực tiếp và sau đó gửi chúng đến máy chủ. Máy chủ xử lý yêu cầu, tự kiểm tra các hành động để đảm bảo chúng không bất hợp pháp và sau đó phát sóng di chuyển tới tất cả người chơi bao gồm cả máy khách.

Điều này có lợi nhất cho cả 1 và 2 với chi phí yêu cầu băng thông cao hơn một chút. Lợi ích là phản hồi tức thì cho khách hàng đối với các động thái của riêng họ và nếu các động thái mà khách hàng thực hiện là bất hợp pháp, máy chủ sẽ thực hiện các hành động thích hợp (sửa lỗi mạnh mẽ cho khách hàng).

Những gì về mất gói và tương tự? Trò chơi mang tính quyết định, nhưng tôi nghĩ rằng bất kỳ sự khác biệt nào trong chuỗi hành động được thực hiện trong máy khách đều có thể dẫn đến các trạng thái không nhất quán trên thế giới. Làm thế nào để tôi bảo vệ chống lại loại vấn đề đó?

Mất gói và độ trễ cực cao là một vấn đề khó khắc phục . Các giải pháp khác nhau (không có giải pháp nào hoàn hảo) được sử dụng để giải quyết vấn đề tùy thuộc vào loại trò chơi (ví dụ: tính toán chết). Tôi sẽ cho rằng trò chơi của bạn không phải đồng bộ hóa vật lý.

Một trong những điều đầu tiên bạn nên làm là dập tắt thời gian cho tất cả các động tác của bạn. Hệ thống của bạn sau đó nên tính đến những điều này và thực hiện các động thái tương ứng (có lẽ máy chủ có trách nhiệm này và sau đó từ chối các động thái bất hợp pháp). Khi thực hiện hệ thống này, giả sử độ trễ 0 (kiểm tra cục bộ).

Tất nhiên độ trễ là 0, không phải là một giả định tốt, ngay cả trên các mạng cục bộ, vì vậy bây giờ tất cả các động thái đều tôn trọng thời gian, bạn tin tưởng vào thời gian nào? Đó là vấn đề đồng bộ hóa thời gian phát huy tác dụng. Nhiều bài viết / giấy tờ trực tuyến sẽ hướng dẫn bạn giải quyết vấn đề này.

Điều gì xảy ra nếu tôi thêm quá nhiều hành động cùng một lúc, sẽ không gây ra sự cố cho kết nối? Có cách nào để giảm bớt điều đó?

Đầu tiên là những thứ rõ ràng. Không bao giờ gửi chuỗi hoặc các đối tượng nối tiếp. Đừng gửi tin nhắn nhanh như trò chơi đang cập nhật. Gửi chúng trong khối (ví dụ 10 lần một giây). Sẽ có lỗi (đặc biệt là lỗi làm tròn) rằng máy chủ / máy khách sẽ cần sửa lỗi một lần (vì vậy mỗi giây, máy chủ sẽ gửi mọi thứ [mọi thứ = tất cả dữ liệu quan trọng để giữ mọi thứ trong trò chơi của bạn chính xác trạng thái] mà khách hàng sau đó chộp lấy và / hoặc tính đến để sửa trạng thái của nó).EDIT: Một trong những đồng nghiệp của tôi đã đề cập rằng nếu bạn đang sử dụng UDP [mà bạn nên, nếu bạn có nhiều dữ liệu] thì không có thứ tự mạng. Gửi hơn 10 gói một giây làm tăng sự thay đổi của các gói không theo thứ tự. Tôi sẽ xem nếu tôi có thể trích dẫn yêu cầu đó. Đối với các gói không theo thứ tự, bạn có thể loại bỏ chúng hoặc thêm nó vào hàng đợi tin nhắn / di chuyển của hệ thống được thiết kế để xử lý các thay đổi trong quá khứ để sửa trạng thái hiện tại

Bạn nên có một hệ thống nhắn tin dựa trên thứ gì đó chiếm rất ít không gian. Nếu bạn phải gửi một cái gì đó chỉ có thể có hai giá trị, thì chỉ gửi một bit. Nếu bạn biết bạn chỉ có thể có 256 di chuyển, thì hãy gửi một ký tự không dấu. Có nhiều thủ thuật vặn vẹo bit khác nhau để đóng gói tin nhắn càng chặt chẽ càng tốt.

Hệ thống nhắn tin của bạn rất có thể sẽ sử dụng rất nhiều enum để cho phép bạn dễ dàng trích xuất các chuyển động từ một tin nhắn đến (tôi sẽ đề nghị xem RakNet và các ví dụ trong đó nếu bạn không hiểu ý tôi ở đây).

Tôi chắc chắn rằng bạn đã biết rằng bạn không thể khắc phục các sự cố phát sinh với độ trễ cao và mất gói nhưng bạn có thể làm cho hiệu ứng của nó giảm đi rõ rệt. Chúc may mắn!

EDIT: Nhận xét của Patrick Hughes ở trên với liên kết thể hiện câu trả lời này không đầy đủ và cụ thể cho câu hỏi của OP. Tôi rất muốn đề nghị đọc các chủ đề được liên kết thay thế


Cảm ơn rất nhiều vì đã trả lời chi tiết, điều đó khá nhiều cho tất cả các mối quan tâm của tôi. Chỉ cần một câu hỏi liên quan đến phần mà bạn nói so every second, the server sends everything ... which the client then snaps to. Tôi có thể gửi một lượng lớn thông tin như thế này cùng một lúc không? Kích thước tối đa hợp lý là gì?
David Gouveia

Mức tối đa hợp lý sẽ là một số không giới thiệu độ trễ :) ... Tôi biết bạn không tìm kiếm câu trả lời đó nhưng tôi không muốn ghi số vì tôi không quen với trò chơi của bạn.
Samaursa

Ngoài ra, không có quy tắc cứng nào là bạn nên gửi một đoạn lớn, tôi chỉ lấy đó làm ví dụ.
Samaursa

@Samaursa - Tôi sẽ không đồng ý với nhận xét "Nếu bạn đang sử dụng UDP [mà bạn nên, nếu bạn có nhiều dữ liệu]". Vì chúng chưa quen với lập trình mạng, TCP chăm sóc hầu hết các công cụ cứng cho bạn với chi phí chậm hơn nhiều. Trong trường hợp của họ, tôi sẽ sử dụng TCP cho đến khi nó trở thành nút cổ chai. Tại thời điểm đó, họ sẽ hiểu rõ hơn về cách ứng dụng của họ đang sử dụng mạng để việc triển khai công cụ UDP sẽ dễ dàng hơn.
Chris

@Chris: Tôi sẽ không đồng ý :) ... quyết định giữa việc sử dụng TCP và UDP giống như (imo) quyết định giữa việc chọn Python và C ++ để phát triển. Về lý thuyết nghe có vẻ ổn, nhưng trong thực tế, thật khó để chuyển qua (một lần nữa, đó là imo).
Samaursa
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.