2/4 XBTGPUARC Full Freeversion for now.

in #deutsch11 days ago

------------------------------------------------------------------------------------->
//--6--mining_job.hpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#pragma once

#include
#include
#include
#include

//--Enthält einen vollständigen MiningJob basierend auf einem Benachrichtigungs Ereignis--

struct MiningJob {
std::string job_id;
std::string prevhash;
std::string coinb1;
std::string coinb2;
std::vector<std::string> merkle_branch;
std::string version;
std::string nbits;
std::string ntime;
std::string extranonce1;
std::string extranonce2;
std::string bits;
};

//--Vergleich: Hash < Target--

inline bool is_valid_hash(const std::array<uint8_t, 32> &hash,
const std::vector<uint8_t> &target) {
for (size_t i = 0; i < 32; ++i) {
if (hash[i] < target[i])
return true;
if (hash[i] > target[i])
return false;
}
return true;
}

//--Wandelt Compact-Format aus "bits" (z. B. 0x1d00ffff) in 256-bit Target!--

inline std::vector<uint8_t> bits_to_target(uint32_t bits) {
uint32_t exponent = bits >> 24;
uint32_t mantissa = bits & 0x007fffff;

std::vector<uint8_t> target(32, 0);

if (exponent <= 3) {

//--Mantisse nach rechts schieben--

mantissa >>= 8 * (3 - exponent);
target[31] = mantissa & 0xFF;
target[30] = (mantissa >> 8) & 0xFF;
target[29] = (mantissa >> 16) & 0xFF;

} else if (exponent <= 32) {

//--Platzierung der Mantisse ab dem richtigen Byte--

int idx = 32 - exponent;
target[idx] = (mantissa >> 16) & 0xFF;
target[idx + 1] = (mantissa >> 8) & 0xFF;
target[idx + 2] = mantissa & 0xFF;

} else {

//--Exponent außerhalb gültigem Rahmens--
//-→Zurück für leeres Ziel (niemals gültig)--

return std::vector<uint8_t>(32, 0xFF);

}

return target;
}

------------------------------------------------------------------------------------->
//--7--notify_parser.hpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
#pragma once

#include "mining_job.hpp"
#include <boost/json.hpp>
#include
#include
#include
#include

//--Diese Funktion parst eine JSON-Zeile vom Pool und wandelt sie in ein--
//-MiningJob-Objekt um.--

inline std::optional parse_notify(const std::string &line) {
using namespace boost::json;
boost::system::error_code ec;
value json_value = parse(line, ec);

if (ec || !json_value.is_object()) {
return std::nullopt; //--Keine gültige JSON-Nachricht--
}

const object &obj = json_value.as_object();

//--Sicherstellen, dass es eine "mining.notify"-Nachricht ist--

if (!obj.contains("method") ||
value_to<std::string>(obj.at("method")) != "mining.notify") {
return std::nullopt;
}

if (!obj.contains("params") || !obj.at("params").is_array()) {
std::cerr << "❌ Fehler: 'mining.notify' hat keine gültigen Parameter.\n";
return std::nullopt;
}
const array &params = obj.at("params").as_array();

//--Die Parameter-Anzahl für BTG auf 2miners.com ist typischerweise 8 oder mehr--
//--Das Programm wird am Ende auf Solo und Poolmining zur Auswahl aufgestockt, bis diese Zeilen Ersetzt wurden.--

if (params.size() < 8) {
std::cerr << "❌ Fehler: 'mining.notify' hat zu wenige Parameter ("
<< params.size() << "). Erwartet >= 8.\n";
return std::nullopt;
}

MiningJob job;

//--Parameterzuweisung LOG
//--Stratum-Protokoll für solo-btg.2miners.com:--
//--params[0]: job_id--
//--params[1]: version--
//--params[2]: prevhash--
//--params[3]: coinb1--
//--params[4]: coinb2--
//--params[5]: nbits--
//--params[6]: ntime--
//--params[7]: clean_job (boolean)--

//-WICHTIG: Dieser Pool sendet KEINEN separaten 'merkle_branch'.--
//-Der Merkle-Root muss vom Miner selbst berechnet werden, indem--
//-die Coinbase-Transaktion gehasht wird. Die 'merkle_branch'--
//-Liste bleibt also absichtlich leer.--

job.job_id = value_to<std::string>(params.at(0));
job.version = value_to<std::string>(params.at(1));
job.prevhash = value_to<std::string>(params.at(2));
job.coinb1 = value_to<std::string>(params.at(3));
job.coinb2 = value_to<std::string>(params.at(4));
job.nbits = value_to<std::string>(params.at(5));
job.ntime = value_to<std::string>(params.at(6));
job.clean_job = params.at(7).as_bool();

//--Die Merkle Branch Liste wird explizit geleert, da sie nicht vom Pool kommt.--

job.merkle_branch.clear();
std::cout << "🌿 Job korrekt geparst. Merkle Branch ist leer, wie vom Pool "
"erwartet.\n";

//--Alte Felder für Kompatibilität füllen--

job.bits = job.nbits;
job.extranonce1 = ""; //--Wird später vom Unterschreiber gesetzt--
job.extranonce2 = "00000000"; //--Platzhalter--

//--Debug-Ausgabe--

std::cout << "🔍 Debug Notify: bits = '" << job.nbits << "', ntime = '"
<< job.ntime << "'\n";

return job;
}

------------------------------------------------------------------------------------->
//--8--opencl_list_devices.cpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#include <CL/cl.h>
#include
#include
#include

void check_error(cl_int err, const std::string &msg) {
if (err != CL_SUCCESS) {
std::cerr << "❌ Fehler: " << msg << " (" << err << ")\n";
exit(1);
}
}

int main() {
cl_uint num_platforms = 0;
cl_int err = clGetPlatformIDs(0, nullptr, &num_platforms);
check_error(err, "clGetPlatformIDs (count)");

std::vector<cl_platform_id> platforms(num_platforms);
err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
check_error(err, "clGetPlatformIDs (fetch)");

std::cout << "🌍 Gefundene OpenCL-Plattformen: " << num_platforms << "\n";

for (cl_uint i = 0; i < num_platforms; ++i) {
char name[128], vendor[128], version[128];
clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, sizeof(name), name,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VERSION, sizeof(version),
version, nullptr);

std::cout << "\n[Plattform " << i << "]\n";
std::cout << "  Name:    " << name << "\n";
std::cout << "  Vendor:  " << vendor << "\n";
std::cout << "  Version: " << version << "\n";

cl_uint num_devices = 0;
err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr,
                     &num_devices);
if (err != CL_SUCCESS || num_devices == 0) {
  std::cout << "  ⚠️  Keine Geräte gefunden.\n";
  continue;
}

std::vector<cl_device_id> devices(num_devices);
clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices,
               devices.data(), nullptr);

for (cl_uint j = 0; j < num_devices; ++j) {
  char devname[128];
  clGetDeviceInfo(devices[j], CL_DEVICE_NAME, sizeof(devname), devname,
                  nullptr);
  std::cout << "    [Device " << j << "] " << devname << "\n";
}

}

return 0;
}

------------------------------------------------------------------------------------->
//--9--opencl_utils.cpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#include "opencl_utils.hpp"
#include "globals.hpp"

#include <CL/cl.h>
#include
#include
#include
#include
#include
#include

namespace {

//--Kleiner Fehler-Helper--

bool cl_ok(cl_int err, const char* where) {
    if (err == CL_SUCCESS) return true;
    std::cerr << "❌ OpenCL Error " << err << " at " << where << "\n";
    return false;
}

//--Lese Datei in string--

std::string read_file(const std::string &path) {
    std::ifstream ifs(path, std::ios::in | std::ios::binary);
    if (!ifs) throw std::runtime_error("Konnte Datei nicht öffnen: " + path);
    return std::string((std::istreambuf_iterator<char>(ifs)),
                        std::istreambuf_iterator<char>());
}

}

//--Größe-Konstanten (gleiche Werte wie globals.hpp)--

constexpr size_t TARGET_SIZE_BYTES = 32;

void init_opencl(const std::string &kernel_path,
const std::string &kernel_func_name,
int platform_index,
int device_index,
int intensity,
GpuResources &resources) {

cl_int err = CL_SUCCESS;

//--Plattformen--

cl_uint num_platforms = 0;
err = clGetPlatformIDs(0, nullptr, &num_platforms);
if (!cl_ok(err, "clGetPlatformIDs(count)")) std::exit(1);
if (num_platforms == 0) {
    std::cerr << "❌ Keine OpenCL Plattformen gefunden\n";
    std::exit(1);
}

std::vector<cl_platform_id> platforms(num_platforms);
err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
if (!cl_ok(err, "clGetPlatformIDs(fetch)")) std::exit(1);
if ((cl_uint)platform_index >= num_platforms) {
    std::cerr << "❌ Ungültiger Plattform-Index\n";
    std::exit(1);
}

cl_platform_id platform = platforms[platform_index];

//--Devices--

cl_uint num_devices = 0;
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, nullptr, &num_devices);
if (!cl_ok(err, "clGetDeviceIDs(count)")) std::exit(1);
if (num_devices == 0) {
    std::cerr << "❌ Keine OpenCL Geräte auf Plattform\n";
    std::exit(1);
}

std::vector<cl_device_id> devices(num_devices);
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, num_devices, devices.data(), nullptr);
if (!cl_ok(err, "clGetDeviceIDs(fetch)")) std::exit(1);

if ((cl_uint)device_index >= num_devices) {
    std::cerr << "❌ Ungültiger Geräte-Index\n";
    std::exit(1);
}

resources.device = devices[device_index];

//--Kontext & Queue--

resources.context = clCreateContext(nullptr, 1, &resources.device, nullptr, nullptr, &err);
if (!cl_ok(err, "clCreateContext")) std::exit(1);

resources.queue = clCreateCommandQueueWithProperties(resources.context, resources.device, nullptr, &err);
if (!cl_ok(err, "clCreateCommandQueueWithProperties")) {
    clReleaseContext(resources.context);
    resources.context = nullptr;
    std::exit(1);
}

//--Kernel-Quelle lesen--

std::string src;
try {
    src = read_file(./XBTGPUARC/kernels/zhash.cl);
} catch (const std::exception &e) {
    std::cerr << "❌ " << e.what() << "\n";
    cleanup_opencl(resources);
    std::exit(1);
}

const char* src_ptr = src.c_str();
size_t src_size = src.size();

resources.program = clCreateProgramWithSource(resources.context, 1, &src_ptr, &src_size, &err);
if (!cl_ok(err, "clCreateProgramWithSource")) {
    cleanup_opencl(resources);
    std::exit(1);
}

//--Build-Optionen (modifizierbar)--

const char* build_opts = "-cl-std=CL2.0 -cl-fast-relaxed-math";
err = clBuildProgram(resources.program, 1, &resources.device, build_opts, nullptr, nullptr);

//--Build-Log ausgeben wenn Fehler oder Info vorhanden--

size_t log_size = 0;
clGetProgramBuildInfo(resources.program, resources.device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
if (log_size > 1) {
    std::vector<char> log(log_size + 1);
    clGetProgramBuildInfo(resources.program, resources.device, CL_PROGRAM_BUILD_LOG, log_size, log.data(), nullptr);
    std::cerr << "--- OpenCL Build Log ---\n" << log.data() << "\n------------------------\n";
}

if (!cl_ok(err, "clBuildProgram")) {
    cleanup_opencl(resources);
    std::exit(1);
}

//--Kernel erstellen--

resources.kernel = clCreateKernel(resources.program, kernel_zhash.cl(), &err);
if (!cl_ok(err, "clCreateKernel")) {
    cleanup_opencl(resources);
    std::exit(1);
}

//--Device-Memory Info (optional: anzeigen)--

cl_ulong mem_total = 0;
if (clGetDeviceInfo(resources.device, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(mem_total), &mem_total, nullptr) == CL_SUCCESS) {
    double mem_mib = mem_total / 1024.0 / 1024.0;
    std::cerr << "🧠 Device VRAM: " << std::fixed << std::setprecision(1) << mem_mib << " MiB\n";
}

//--Puffergrößen anlegen (INPUT_SIZE / HASH_SIZE aus globals.hpp)--

size_t in_size = static_cast<size_t>(INPUT_SIZE) * std::max(1, intensity);
size_t out_hashes_size = static_cast<size_t>(HASH_SIZE) * std::max(1, intensity);

resources.input_buffer = clCreateBuffer(resources.context, CL_MEM_READ_WRITE, in_size, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(input_buffer)")) { cleanup_opencl(resources); std::exit(1); }

resources.output_hashes_buffer = clCreateBuffer(resources.context, CL_MEM_READ_WRITE, out_hashes_size, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(output_hashes_buffer)")) { cleanup_opencl(resources); std::exit(1); }

resources.pool_target_buffer = clCreateBuffer(resources.context, CL_MEM_READ_ONLY, TARGET_SIZE_BYTES, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(pool_target_buffer)")) { cleanup_opencl(resources); std::exit(1); }

std::cout << "✅ OpenCL initialisiert (Kernel: " << zhash.cl << ")\n";

}

void cleanup_opencl(GpuResources &resources) {
if (resources.input_buffer) { clReleaseMemObject(resources.input_buffer); resources.input_buffer = nullptr; }
if (resources.output_buffer) { clReleaseMemObject(resources.output_buffer); resources.output_buffer = nullptr; } // falls verwendet
if (resources.output_hashes_buffer) { clReleaseMemObject(resources.output_hashes_buffer); resources.output_hashes_buffer = nullptr; }
if (resources.pool_target_buffer) { clReleaseMemObject(resources.pool_target_buffer); resources.pool_target_buffer = nullptr; }

if (resources.kernel) { clReleaseKernel(resources.kernel); resources.kernel = nullptr; }
if (resources.program) { clReleaseProgram(resources.program); resources.program = nullptr; }
if (resources.queue) { clReleaseCommandQueue(resources.queue); resources.queue = nullptr; }
if (resources.context) { clReleaseContext(resources.context); resources.context = nullptr; }

resources.device = nullptr;

}

------------------------------------------------------------------------------------->
//--10--opencl_utils.hpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#pragma once

#include
#include <CL/cl.h>
#include "globals.hpp"

//--Initialisiert OpenCL (einheitliche Signatur)--
//--kernel_path: Pfad zur .cl Datei--
//--kernel_func_name: Name der Kernel-Funktion inside the .cl (z.B. "zhash.cl")--
//--platform_index / device_index: Auswahl--
//--intensity: wieviele Einheiten (wird für Puffergrößen verwendet)--

void init_opencl(const std::string& kernel_path,
const std::string& kernel_func_name,
int platform_index,
int device_index,
int intensity,
GpuResources& resources);

//--Gibt alle OpenCL-Ressourcen frei (sicher mehrfach aufrufbar)--

void cleanup_opencl(GpuResources& resources);

//--Schreibt das hex-Target in GPU-Puffer (bestehende Signatur)--

void update_opencl_target(const GpuResources& resources, const std::string& hex_target);

//--Optional: kleines Helfer-Interface zum Setzen der Kernel-Args (wenn benötigt)--

void set_kernel_args(const GpuResources& resources,
cl_mem solution_indexes_buffer,
uint32_t start_nonce);

------------------------------------------------------------------------------------->
//--11--run.sh--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#!/bin/bash

export GPU_MAX_HEAP_SIZE=100 //--
export GPU_MAX_USE_SYNC_OBJECTS=1 //--
export GPU_SINGLE_ALLOC_PERCENT=100 //--
export GPU_MAX_ALLOC_PERCENT=100 //--
export GPU_MAX_SINGLE_ALLOC_PERCENT=100 //--
export GPU_ENABLE_LARGE_ALLOCATION=100 //--
export GPU_MAX_WORKGROUP_SIZE=64 //--

./xbtgpuarc \ //--Startet das XBTGPUARC Mining Programm.--
--platform 1 \ //--Wählt die Plattform aus, auf wlecher Gemined werden soll.--
--device 0 \ //-Wählt die genaue Recheneinheit in Form der Intel ARC Grafikkarte mit dem DG2 Chip aus.--
--algo zhash_144_5 \ //--Wählt den zu minenden Algoritmus aus--
--pool solo-btg.2miners.com \ //--Wählt den Pool oder den Server aus, um mit dem Netzwerk zu Kommunizieren.--
--port 4040 \ //--Wählt den PoolPort aus.--
--wallet Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8 \ //--Hier fügen Sie ihre eigene Mining Adressen ein!--
//--Sehr Wichtig, da ansonsten die Belohnung an meine Person geht!--
--worker A730m \ //--Hier sehen Sie die genaue Bezeichnung des ausgewählten ARC Computer Chips.--
--password x \ //--Hier können Sie den Wert bei "x" Behalten. Es würde wohl kaum jemand ihre Wallet mit Geld durch seine Arbeit bei ihnen füllen wollen.--
--intensity 256 //--HIer kann man Einstellen, wie Stark die Grafikkarte arbeiten soll!--
//--Ein Mittelhoher Wert ist in der Regel zu Präferieren, genaue Details zur ARC GPU Architektur stehen noch aus.--


//--12--stratum_notify_listener.cpp--


#include "stratum_notify_listener.hpp"
#include "globals.hpp"
#include "miner_loop.hpp"
#include "notify_parser.hpp" //--Wir nutzen jetzt den externen Parser!--
#include "opencl_utils.hpp"

#include <boost/asio.hpp>
#include <boost/json.hpp>
#include
#include
#include
#include
#include

using boost::asio::ip::tcp;

//--Globale Variablen, die von anderen Teilen des Programms gesetzt werden--

extern int next_request_id;
extern std::string current_job_id;
extern std::string worker_name;

//--Funktion zum Senden eines gefundenen Shares an den Pool--

void submit_share(tcp::socket &socket, const std::string &nonce_hex,
const std::string &ntime_hex, const std::string &job_id) {
using namespace boost::json;

//--Erstellt die Parameter für die "mining.submit" Methode--

array params;
params.emplace_back(worker_name);
params.emplace_back(job_id);
params.emplace_back(
"00000000"); //--extranonce2 Platzhalter, oft nicht benötigt--
params.emplace_back(ntime_hex);
params.emplace_back(nonce_hex);

//--Baut die komplette JSON-RPC-Anfrage zusammen--

object request;
request["id"] = next_request_id++;
request["method"] = "mining.submit";
request["params"] = params;

//--Sendet die Nachricht an den Pool--

std::string message = serialize(request) + "\n";
boost::asio::write(socket, boost::asio::buffer(message));
std::cout << "📤 Share für Job " << job_id << " gesendet:\n" << message;
}

//--Hauptfunktion, die die Verbindung zum Stratum-Pool hält und auf Nachrichten lauscht--

void run_stratum_listener(const std::string &pool_host, int pool_port,
const std::string &wallet, const std::string &worker,
const std::string &password, int intensity,
GpuResources &gpu_resources) {
const std::string port_str = std::to_string(pool_port);
worker_name = wallet + "." + worker; //--Setzt den globalen Worker-Namen--

try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(pool_host, port_str);
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoints);

std::cout << "📡 Verbunden mit " << pool_host << ":" << port_str << "\n";

//--Standard-Nachrichten zur Anmeldung am Pool--

std::string subscribe =
    R"({"id": 1, "method": "mining.subscribe", "params": []})"
    "\n";
std::string authorize =
    R"({"id": 2, "method": "mining.authorize", "params": [")" +
    worker_name + R"(", ")" + password +
    R"("]})"
    "\n";

boost::asio::write(socket, boost::asio::buffer(subscribe));
boost::asio::write(socket, boost::asio::buffer(authorize));

std::string buffer; //--Puffer für eingehende Daten vom Socket--
static std::thread mining_thread;

//--Endlosschleife zum Lesen von Nachrichten--

for (;;) {
  char reply[4096];
  boost::system::error_code error;
  size_t len = socket.read_some(boost::asio::buffer(reply), error);
  if (len == 0 && error)
    break; //--Verbindung geschlossen oder Fehler--

  buffer.append(reply, len);
  size_t pos = 0;

  //--Verarbeite jede vollständige Zeile (getrennt durch '\n') im Puffer--

  while ((pos = buffer.find('\n')) != std::string::npos) {
    std::string line = buffer.substr(0, pos);
    buffer.erase(0, pos + 1);
    std::cout << "🌐 Nachricht:\n" << line << "\n";

    //--Versuch, die Nachricht als Mining-Job zu parsen--

    auto job_opt = parse_notify(line);

    if (job_opt) { //--Wenn ein gültiger Job empfangen wurde--
      auto &job = *job_opt;
      current_job_id = job.job_id; //--Update der globalen Job-ID--

      std::cout << "🎯 Job ID: " << job.job_id << "\n";
      std::cout << "🧱 PrevHash: " << job.prevhash << "\n";

      //--Stoppe den alten Mining-Prozess, falls er noch läuft--

      if (mining_thread.joinable()) {
        stop_mining();
        mining_thread.join();
      }

      //--Definiere eine Lambda-Funktion, die aufgerufen wird, wenn eine--
      //-Lösung gefunden wird!!!--

      auto share_submitter = [&](uint32_t nonce,
                                 const std::array<uint8_t, 32> &hash,
                                 const MiningJob &job) {
        std::stringstream ss_nonce;
        ss_nonce << std::hex << std::setw(8) << std::setfill('0') << nonce;

        //--Keine Umwandlung von job.ntime nötig, ist schon hex-String--

        submit_share(socket, ss_nonce.str(), job.ntime, job.job_id);
      };

      //--Starte den neuen Mining-Prozess in einem eigenen Thread--

      mining_thread = std::thread([&, job]() {
        miner_loop(job, share_submitter, gpu_resources, intensity);
      });
    }
  }
  if (error == boost::asio::error::eof)
    break;
  else if (error)
    throw boost::system::system_error(error);
}

if (mining_thread.joinable()) {
  stop_mining();
  mining_thread.join();
}

} catch (const std::exception &e) {
std::cerr << "❌ Stratum-Fehler: " << e.what() << "\n";
}
}

------------------------------------------------------------------------------------->
//--13--stratum_notify_listener.hpp--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

#pragma once
#include "opencl_utils.hpp"
#include

//--Startet Stratum-Connection, empfängt Jobs, startet Miner, horcht auf Arbeit vom Pool.--

void run_stratum_listener(const std::string &pool_host, int pool_port,
const std::string &wallet, const std::string &worker,
const std::string &password, int intensity,
GpuResources &gpu_resources); //--← NICHT const--
------------------------------------------------------------------------------------->

SCHLUSS 2/4