Làm cách nào để tôi biên dịch tập lệnh C # trong thời gian chạy và đính kèm nó làm thành phần cho đối tượng trò chơi


8

Ý tưởng ở đây là cho phép người dùng lập trình hành vi của một tác nhân theo cách gợi nhớ đến trò chơi "Screeps", nơi bạn có thể phát triển mã mà sau này chạy và ra lệnh cho hành vi của các đơn vị của bạn.

Ý tưởng của tôi là cho phép người dùng phát triển hành vi hoặc đơn vị của họ bằng mã C #, biên dịch mã đó khi chạy và đính kèm dưới dạng MonoBehavior vào một đối tượng trò chơi hiện có.

Làm thế nào tôi có thể đạt được điều này?


3
Bạn cũng có thể sử dụng một ngôn ngữ kịch bản hiện có. Ví dụ: có JavaScript.net cho phép bạn diễn giải và thực thi JavaScripts từ chương trình C #. Pro: nỗ lực triển khai tối thiểu, bạn có thể giới thiệu người dùng đến tài liệu học tập hiện có của JS. Con: Có thể quá mạnh mẽ và quá khó hiểu cho trường hợp sử dụng hạn chế của bạn.
Philipp

1
Một tùy chọn khác là tạo một trình soạn thảo trực quan nơi người dùng kéo và thả xung quanh các khối hướng dẫn. Pro: Trông và cảm thấy giống như một trò chơi, dễ dạy hơn, dễ học hơn cho những người không lập trình. Contra: Mất thời gian để phát triển, có thể quá giới hạn cho các lập trình viên có kinh nghiệm (và những người trở thành nó bằng cách chơi trò chơi).
Philipp

Một trong những ý tưởng của tôi trong đó các khối hướng dẫn chính xác có ưu và nhược điểm, cho phép người dùng tạo khối riêng sẽ có lợi ích từ cả hai ý tưởng @Philipp cung cấp, tôi có đúng hay tôi đang thiếu điều gì đó khi đưa ra tuyên bố đó?
Oscar Guillamon

"Làm thế nào để đạt được điều này" là một câu hỏi cực kỳ rộng. Có một số khía cạnh độc lập với vấn đề này, như thiết kế giao diện người dùng, quyết định khối nào bạn cần, cách biểu diễn chúng dưới dạng cấu trúc dữ liệu và cách diễn giải các cấu trúc dữ liệu này. Bạn cần chỉ định những khía cạnh nào bạn gặp vấn đề và vấn đề đó là gì.
Philipp

Câu trả lời:


12

Tôi đã không tự mình thực hiện điều này, vì vậy câu trả lời này dựa trên một bài báo trước đó ( Tôi chỉ viết "biên dịch thời gian chạy thống nhất" và tìm thấy 4 câu trả lời ngay trên đầu, vì vậy đừng bỏ qua việc thử tự tìm kiếm!)

Những gì bạn mô tả là có thể, nhưng không phải không có cạm bẫy:

  • Sử dụng mức tương thích .Net 2.0, nếu không, một số không gian tên bạn cần có thể vắng mặt.

  • Không phải mọi nền tảng sẽ cho phép bạn biên dịch C # mới từ các chuỗi khi chạy - cụ thể là Android và iOS không cho phép điều này.

  • Ngoài hộp này hoạt động trên Windows, nhưng không phải trên máy Mac / Linux trừ khi người dùng đã cài đặt SDK Mono. Bạn có thể bao gồm mcs-ICodeCompiler của aeroson để xây dựng chức năng này vào trò chơi của bạn dưới dạng plugin.

mã ví dụ của exodrifter (được sửa đổi để thêm một thành phần)

(tham khảo bài viết để biết chi tiết đầy đủ - Tôi chỉ muốn bao gồm đủ ở đây rằng câu trả lời này là một biển chỉ dẫn hữu ích ngay cả khi bài viết được liên kết đi xuống)

using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;
using UnityEngine;

public class RuntimeCompileTest : MonoBehaviour
{
    void Start()
    {
        var assembly = Compile(@"
        using UnityEngine;

        public class RuntimeCompiled : MonoBehaviour
        {
            public static RuntimeCompiled AddYourselfTo(GameObject host)
            {
                return host.AddComponent<RuntimeCompiled>();
            }

            void Start()
            {
                Debug.Log(""The runtime compiled component was successfully attached to"" + gameObject.name);
            }
        }");    

        var runtimeType = assembly.GetType("RuntimeCompiled");
        var method = runtimeType.GetMethod("AddYourselfTo");
        var del = (Func<GameObject, MonoBehaviour>)
                      Delegate.CreateDelegate(
                          typeof(Func<GameObject, MonoBehaviour>), 
                          method
                  );

        // We ask the compiled method to add its component to this.gameObject
        var addedComponent = del.Invoke(gameObject);

        // The delegate pre-bakes the reflection, so repeated calls don't
        // cost us every time, as long as we keep re-using the delegate.
    }

    public static Assembly Compile(string source)
    {
        // Replace this Compiler.CSharpCodeProvider wth aeroson's version
        // if you're targeting non-Windows platforms:
        var provider = new CSharpCodeProvider();
        var param = new CompilerParameters();

        // Add ALL of the assembly references
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            param.ReferencedAssemblies.Add(assembly.Location);
        }

        // Or, uncomment just the assemblies you need...

        // System namespace for common types like collections.
        //param.ReferencedAssemblies.Add("System.dll");

        // This contains methods from the Unity namespaces:
        //param.ReferencedAssemblies.Add("UnityEngines.dll");

        // This assembly contains runtime C# code from your Assets folders:
        // (If you're using editor scripts, they may be in another assembly)
        //param.ReferencedAssemblies.Add("CSharp.dll");


        // Generate a dll in memory
        param.GenerateExecutable = false;
        param.GenerateInMemory = true;

        // Compile the source
        var result = provider.CompileAssemblyFromSource(param, source);

        if (result.Errors.Count > 0) {
            var msg = new StringBuilder();
            foreach (CompilerError error in result.Errors) {
                msg.AppendFormat("Error ({0}): {1}\n",
                    error.ErrorNumber, error.ErrorText);
            }
            throw new Exception(msg.ToString());
        }

        // Return the assembly
        return result.CompiledAssembly;
    }
}

Cảnh báo

Hãy rất cẩn thận khi để cho trò chơi của bạn biên dịch & chạy các kịch bản bạn đã không viết cho mình. Các tập lệnh này có thể được thực hiện để làm bất cứ điều gì mà tài khoản người dùng của người chơi có đặc quyền phải làm - xóa hoặc làm hỏng tệp người dùng, tải xuống và chạy phần mềm độc hại, rình mò thông tin cá nhân, tất cả điều đó.

Nhiều như chúng tôi có thể cố gắng chặn và phát hiện những trường hợp này, tôi không bao giờ đặc biệt tin rằng chúng tôi đã nắm bắt được tất cả - luôn có khả năng một số tính năng ngôn ngữ C # tôi không biết về việc cho phép chúng truy cập các tính năng mà tôi nghĩ đã bị cắt .

Nếu tập lệnh chỉ được tạo bởi người chơi cục bộ, thì họ có thể phá game theo cách tùy ý, nhưng không thể làm gì với máy tính của họ, dù sao họ cũng không được phép làm. Nhưng nếu người chơi có thể chia sẻ tập lệnh, tải xuống từ tài nguyên mạng hoặc có thể bị lừa sao chép và dán tập lệnh mà họ không viết, thì đây sẽ trở thành một vectơ tấn công có thể bị khai thác.

Nếu bạn muốn cho phép người chơi lập trình mà không cần mở loại lỗ hổng này, hãy xem xét việc viết máy ảo của riêng bạn để phân tích / giải thích / thực thi các tập lệnh của trình phát trong một môi trường hạn chế hơn mà bạn kiểm soát hoàn toàn.


Cảm ơn về đầu vào, đặc biệt là về các cảnh báo bạn đã đưa cho tôi, tôi đã có ý tưởng về chúng nhưng không ngắn gọn như bạn đã nêu
Oscar Guillamon
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.