Cocos Creator3.8 项目实战(十)使用 protobuf详细教程


在 Cocos Creator 中使用 protobuf.js 库可以方便地进行协议的序列化和反序列化。

下面是使用 protobuf.js 的详细说明:


一、protobuf环境安装

1、安装 npm

protobuf环境安装安装需要使用 npm 命令进行,因此首先需要安装 npm 。

如果你还没安装 npm , 请参考:windows安装npm教程 。


2、全局安装pbjs

打开命令行工具,输入以下命令

npm install -g pbjs

安装成功后 C:\Users\Administrator\AppData\Roaming\npm 显示如下信息:

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


二、 Cocos Creator 中使用 protobuf.js 库


确定了模块格式和导出方式,就可以在脚本资源里使用 protobufjs 这个模块。


1、创建协议文件

在项目中创建一个 .proto 文件,定义协议结构

例如,创建一个名为 message.proto 的文件,内容如下:

syntax = "proto3";message Player {string name = 1;int32 level = 2;
}
```

这里定义了一个名为 Player 的消息类型,包含 namelevel 两个字段。


在这里插入图片描述

2、将 protobuf 中的 proto 文件编译成TypeScript 文件


pbjs 指令的使用可以查看 官方文档:pbjs地址

在这里插入图片描述


我们以打包上面生成的 message.proto 文件为例,进行演示。


命令行执行:

pbjs D:/test/3/proto/message.proto  --ts   D:/test/3/proto/message.ts  

打包完成后,在 D:/test/3/proto/ 目录,生成了 message.ts 文件,如下:

在这里插入图片描述


message.ts 文件内容如下:

export interface Player {name?: string;level?: number;
}export function encodePlayer(message: Player): Uint8Array {let bb = popByteBuffer();_encodePlayer(message, bb);return toUint8Array(bb);
}function _encodePlayer(message: Player, bb: ByteBuffer): void {// optional string name = 1;let $name = message.name;if ($name !== undefined) {writeVarint32(bb, 10);writeString(bb, $name);}// optional int32 level = 2;let $level = message.level;if ($level !== undefined) {writeVarint32(bb, 16);writeVarint64(bb, intToLong($level));}
}export function decodePlayer(binary: Uint8Array): Player {return _decodePlayer(wrapByteBuffer(binary));
}function _decodePlayer(bb: ByteBuffer): Player {let message: Player = {} as any;end_of_message: while (!isAtEnd(bb)) {let tag = readVarint32(bb);switch (tag >>> 3) {case 0:break end_of_message;// optional string name = 1;case 1: {message.name = readString(bb, readVarint32(bb));break;}// optional int32 level = 2;case 2: {message.level = readVarint32(bb);break;}default:skipUnknownField(bb, tag & 7);}}return message;
}export interface Long {low: number;high: number;unsigned: boolean;
}interface ByteBuffer {bytes: Uint8Array;offset: number;limit: number;
}function pushTemporaryLength(bb: ByteBuffer): number {let length = readVarint32(bb);let limit = bb.limit;bb.limit = bb.offset + length;return limit;
}function skipUnknownField(bb: ByteBuffer, type: number): void {switch (type) {case 0: while (readByte(bb) & 0x80) { } break;case 2: skip(bb, readVarint32(bb)); break;case 5: skip(bb, 4); break;case 1: skip(bb, 8); break;default: throw new Error("Unimplemented type: " + type);}
}function stringToLong(value: string): Long {return {low: value.charCodeAt(0) | (value.charCodeAt(1) << 16),high: value.charCodeAt(2) | (value.charCodeAt(3) << 16),unsigned: false,};
}function longToString(value: Long): string {let low = value.low;let high = value.high;return String.fromCharCode(low & 0xFFFF,low >>> 16,high & 0xFFFF,high >>> 16);
}// The code below was modified from https://github.com/protobufjs/bytebuffer.js
// which is under the Apache License 2.0.let f32 = new Float32Array(1);
let f32_u8 = new Uint8Array(f32.buffer);let f64 = new Float64Array(1);
let f64_u8 = new Uint8Array(f64.buffer);function intToLong(value: number): Long {value |= 0;return {low: value,high: value >> 31,unsigned: value >= 0,};
}let bbStack: ByteBuffer[] = [];function popByteBuffer(): ByteBuffer {const bb = bbStack.pop();if (!bb) return { bytes: new Uint8Array(64), offset: 0, limit: 0 };bb.offset = bb.limit = 0;return bb;
}function pushByteBuffer(bb: ByteBuffer): void {bbStack.push(bb);
}function wrapByteBuffer(bytes: Uint8Array): ByteBuffer {return { bytes, offset: 0, limit: bytes.length };
}function toUint8Array(bb: ByteBuffer): Uint8Array {let bytes = bb.bytes;let limit = bb.limit;return bytes.length === limit ? bytes : bytes.subarray(0, limit);
}function skip(bb: ByteBuffer, offset: number): void {if (bb.offset + offset > bb.limit) {throw new Error('Skip past limit');}bb.offset += offset;
}function isAtEnd(bb: ByteBuffer): boolean {return bb.offset >= bb.limit;
}function grow(bb: ByteBuffer, count: number): number {let bytes = bb.bytes;let offset = bb.offset;let limit = bb.limit;let finalOffset = offset + count;if (finalOffset > bytes.length) {let newBytes = new Uint8Array(finalOffset * 2);newBytes.set(bytes);bb.bytes = newBytes;}bb.offset = finalOffset;if (finalOffset > limit) {bb.limit = finalOffset;}return offset;
}function advance(bb: ByteBuffer, count: number): number {let offset = bb.offset;if (offset + count > bb.limit) {throw new Error('Read past limit');}bb.offset += count;return offset;
}function readBytes(bb: ByteBuffer, count: number): Uint8Array {let offset = advance(bb, count);return bb.bytes.subarray(offset, offset + count);
}function writeBytes(bb: ByteBuffer, buffer: Uint8Array): void {let offset = grow(bb, buffer.length);bb.bytes.set(buffer, offset);
}function readString(bb: ByteBuffer, count: number): string {// Sadly a hand-coded UTF8 decoder is much faster than subarray+TextDecoder in V8let offset = advance(bb, count);let fromCharCode = String.fromCharCode;let bytes = bb.bytes;let invalid = '\uFFFD';let text = '';for (let i = 0; i < count; i++) {let c1 = bytes[i + offset], c2: number, c3: number, c4: number, c: number;// 1 byteif ((c1 & 0x80) === 0) {text += fromCharCode(c1);}// 2 byteselse if ((c1 & 0xE0) === 0xC0) {if (i + 1 >= count) text += invalid;else {c2 = bytes[i + offset + 1];if ((c2 & 0xC0) !== 0x80) text += invalid;else {c = ((c1 & 0x1F) << 6) | (c2 & 0x3F);if (c < 0x80) text += invalid;else {text += fromCharCode(c);i++;}}}}// 3 byteselse if ((c1 & 0xF0) == 0xE0) {if (i + 2 >= count) text += invalid;else {c2 = bytes[i + offset + 1];c3 = bytes[i + offset + 2];if (((c2 | (c3 << 8)) & 0xC0C0) !== 0x8080) text += invalid;else {c = ((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);if (c < 0x0800 || (c >= 0xD800 && c <= 0xDFFF)) text += invalid;else {text += fromCharCode(c);i += 2;}}}}// 4 byteselse if ((c1 & 0xF8) == 0xF0) {if (i + 3 >= count) text += invalid;else {c2 = bytes[i + offset + 1];c3 = bytes[i + offset + 2];c4 = bytes[i + offset + 3];if (((c2 | (c3 << 8) | (c4 << 16)) & 0xC0C0C0) !== 0x808080) text += invalid;else {c = ((c1 & 0x07) << 0x12) | ((c2 & 0x3F) << 0x0C) | ((c3 & 0x3F) << 0x06) | (c4 & 0x3F);if (c < 0x10000 || c > 0x10FFFF) text += invalid;else {c -= 0x10000;text += fromCharCode((c >> 10) + 0xD800, (c & 0x3FF) + 0xDC00);i += 3;}}}}else text += invalid;}return text;
}function writeString(bb: ByteBuffer, text: string): void {// Sadly a hand-coded UTF8 encoder is much faster than TextEncoder+set in V8let n = text.length;let byteCount = 0;// Write the byte count firstfor (let i = 0; i < n; i++) {let c = text.charCodeAt(i);if (c >= 0xD800 && c <= 0xDBFF && i + 1 < n) {c = (c << 10) + text.charCodeAt(++i) - 0x35FDC00;}byteCount += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;}writeVarint32(bb, byteCount);let offset = grow(bb, byteCount);let bytes = bb.bytes;// Then write the bytesfor (let i = 0; i < n; i++) {let c = text.charCodeAt(i);if (c >= 0xD800 && c <= 0xDBFF && i + 1 < n) {c = (c << 10) + text.charCodeAt(++i) - 0x35FDC00;}if (c < 0x80) {bytes[offset++] = c;} else {if (c < 0x800) {bytes[offset++] = ((c >> 6) & 0x1F) | 0xC0;} else {if (c < 0x10000) {bytes[offset++] = ((c >> 12) & 0x0F) | 0xE0;} else {bytes[offset++] = ((c >> 18) & 0x07) | 0xF0;bytes[offset++] = ((c >> 12) & 0x3F) | 0x80;}bytes[offset++] = ((c >> 6) & 0x3F) | 0x80;}bytes[offset++] = (c & 0x3F) | 0x80;}}
}function writeByteBuffer(bb: ByteBuffer, buffer: ByteBuffer): void {let offset = grow(bb, buffer.limit);let from = bb.bytes;let to = buffer.bytes;// This for loop is much faster than subarray+set on V8for (let i = 0, n = buffer.limit; i < n; i++) {from[i + offset] = to[i];}
}function readByte(bb: ByteBuffer): number {return bb.bytes[advance(bb, 1)];
}function writeByte(bb: ByteBuffer, value: number): void {let offset = grow(bb, 1);bb.bytes[offset] = value;
}function readFloat(bb: ByteBuffer): number {let offset = advance(bb, 4);let bytes = bb.bytes;// Manual copying is much faster than subarray+set in V8f32_u8[0] = bytes[offset++];f32_u8[1] = bytes[offset++];f32_u8[2] = bytes[offset++];f32_u8[3] = bytes[offset++];return f32[0];
}function writeFloat(bb: ByteBuffer, value: number): void {let offset = grow(bb, 4);let bytes = bb.bytes;f32[0] = value;// Manual copying is much faster than subarray+set in V8bytes[offset++] = f32_u8[0];bytes[offset++] = f32_u8[1];bytes[offset++] = f32_u8[2];bytes[offset++] = f32_u8[3];
}function readDouble(bb: ByteBuffer): number {let offset = advance(bb, 8);let bytes = bb.bytes;// Manual copying is much faster than subarray+set in V8f64_u8[0] = bytes[offset++];f64_u8[1] = bytes[offset++];f64_u8[2] = bytes[offset++];f64_u8[3] = bytes[offset++];f64_u8[4] = bytes[offset++];f64_u8[5] = bytes[offset++];f64_u8[6] = bytes[offset++];f64_u8[7] = bytes[offset++];return f64[0];
}function writeDouble(bb: ByteBuffer, value: number): void {let offset = grow(bb, 8);let bytes = bb.bytes;f64[0] = value;// Manual copying is much faster than subarray+set in V8bytes[offset++] = f64_u8[0];bytes[offset++] = f64_u8[1];bytes[offset++] = f64_u8[2];bytes[offset++] = f64_u8[3];bytes[offset++] = f64_u8[4];bytes[offset++] = f64_u8[5];bytes[offset++] = f64_u8[6];bytes[offset++] = f64_u8[7];
}function readInt32(bb: ByteBuffer): number {let offset = advance(bb, 4);let bytes = bb.bytes;return (bytes[offset] |(bytes[offset + 1] << 8) |(bytes[offset + 2] << 16) |(bytes[offset + 3] << 24));
}function writeInt32(bb: ByteBuffer, value: number): void {let offset = grow(bb, 4);let bytes = bb.bytes;bytes[offset] = value;bytes[offset + 1] = value >> 8;bytes[offset + 2] = value >> 16;bytes[offset + 3] = value >> 24;
}function readInt64(bb: ByteBuffer, unsigned: boolean): Long {return {low: readInt32(bb),high: readInt32(bb),unsigned,};
}function writeInt64(bb: ByteBuffer, value: Long): void {writeInt32(bb, value.low);writeInt32(bb, value.high);
}function readVarint32(bb: ByteBuffer): number {let c = 0;let value = 0;let b: number;do {b = readByte(bb);if (c < 32) value |= (b & 0x7F) << c;c += 7;} while (b & 0x80);return value;
}function writeVarint32(bb: ByteBuffer, value: number): void {value >>>= 0;while (value >= 0x80) {writeByte(bb, (value & 0x7f) | 0x80);value >>>= 7;}writeByte(bb, value);
}function readVarint64(bb: ByteBuffer, unsigned: boolean): Long {let part0 = 0;let part1 = 0;let part2 = 0;let b: number;b = readByte(bb); part0 = (b & 0x7F); if (b & 0x80) {b = readByte(bb); part0 |= (b & 0x7F) << 7; if (b & 0x80) {b = readByte(bb); part0 |= (b & 0x7F) << 14; if (b & 0x80) {b = readByte(bb); part0 |= (b & 0x7F) << 21; if (b & 0x80) {b = readByte(bb); part1 = (b & 0x7F); if (b & 0x80) {b = readByte(bb); part1 |= (b & 0x7F) << 7; if (b & 0x80) {b = readByte(bb); part1 |= (b & 0x7F) << 14; if (b & 0x80) {b = readByte(bb); part1 |= (b & 0x7F) << 21; if (b & 0x80) {b = readByte(bb); part2 = (b & 0x7F); if (b & 0x80) {b = readByte(bb); part2 |= (b & 0x7F) << 7;}}}}}}}}}return {low: part0 | (part1 << 28),high: (part1 >>> 4) | (part2 << 24),unsigned,};
}function writeVarint64(bb: ByteBuffer, value: Long): void {let part0 = value.low >>> 0;let part1 = ((value.low >>> 28) | (value.high << 4)) >>> 0;let part2 = value.high >>> 24;// ref: src/google/protobuf/io/coded_stream.cclet size =part2 === 0 ?part1 === 0 ?part0 < 1 << 14 ?part0 < 1 << 7 ? 1 : 2 :part0 < 1 << 21 ? 3 : 4 :part1 < 1 << 14 ?part1 < 1 << 7 ? 5 : 6 :part1 < 1 << 21 ? 7 : 8 :part2 < 1 << 7 ? 9 : 10;let offset = grow(bb, size);let bytes = bb.bytes;switch (size) {case 10: bytes[offset + 9] = (part2 >>> 7) & 0x01;case 9: bytes[offset + 8] = size !== 9 ? part2 | 0x80 : part2 & 0x7F;case 8: bytes[offset + 7] = size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F;case 7: bytes[offset + 6] = size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F;case 6: bytes[offset + 5] = size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F;case 5: bytes[offset + 4] = size !== 5 ? part1 | 0x80 : part1 & 0x7F;case 4: bytes[offset + 3] = size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F;case 3: bytes[offset + 2] = size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F;case 2: bytes[offset + 1] = size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F;case 1: bytes[offset] = size !== 1 ? part0 | 0x80 : part0 & 0x7F;}
}function readVarint32ZigZag(bb: ByteBuffer): number {let value = readVarint32(bb);// ref: src/google/protobuf/wire_format_lite.hreturn (value >>> 1) ^ -(value & 1);
}function writeVarint32ZigZag(bb: ByteBuffer, value: number): void {// ref: src/google/protobuf/wire_format_lite.hwriteVarint32(bb, (value << 1) ^ (value >> 31));
}function readVarint64ZigZag(bb: ByteBuffer): Long {let value = readVarint64(bb, /* unsigned */ false);let low = value.low;let high = value.high;let flip = -(low & 1);// ref: src/google/protobuf/wire_format_lite.hreturn {low: ((low >>> 1) | (high << 31)) ^ flip,high: (high >>> 1) ^ flip,unsigned: false,};
}function writeVarint64ZigZag(bb: ByteBuffer, value: Long): void {let low = value.low;let high = value.high;let flip = high >> 31;// ref: src/google/protobuf/wire_format_lite.hwriteVarint64(bb, {low: (low << 1) ^ flip,high: ((high << 1) | (low >>> 31)) ^ flip,unsigned: false,});
}

3、代码中测试打包出来的typescript脚本


因为pbjs打包后protobufjs代码是直接集成在 message.ts 文件中的,因此不需要我们再去引用protobufjs库。


import { _decorator, Component, log } from 'cc';
const { ccclass, property } = _decorator;import{Player,encodePlayer,decodePlayer} from "./message"@ccclass('main')
export class main extends Component {onLoad() {const player:Player = {name:"John",level:10,} // 将消息对象序列化为二进制数据const binaryData:Uint8Array = encodePlayer(player);// 从二进制数据中反序列化为消息对象const deserializedPlayer = decodePlayer(binaryData);// 访问消息对象的字段console.log(deserializedPlayer.name); // 输出: Johnconsole.log(deserializedPlayer.level); // 输出: 10}start() {}update(deltaTime: number) {}
}

运行打印结果如下:

在这里插入图片描述


好了,Cocos Creator 3.8中如何如何将 proto文件打包成typescript 脚本,以及如何使用 protobuf 的教程到此就结束。如果觉得我的博文帮到了您,您的赞关注是对我最大的支持。如遇到什么问题,可评论区留言。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/110724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信一键群发超过200人的方法

在当前社交媒体的大潮中&#xff0c;微信已跻身全球最热门的社交平台之列。对于企业的运营管理来说&#xff0c;微信的管理系统工具发挥着举足轻重的作用。然而&#xff0c;微信自带的群发助手功能最多只能发送消息给200个好友&#xff0c;要是需要发送1000个好友甚至更多&…

《论文阅读28》OGMM

一、论文 研究领域&#xff1a; 点云配准 | 有监督 部分重叠论文&#xff1a;Overlap-guided Gaussian Mixture Models for Point Cloud Registration WACV 2023 二、概述 概率3D点云配准方法在克服噪声、异常值和密度变化方面表现出有竞争力的性能。本文将点云对的配准问题…

10款远程办公软件,助你事半功倍,晋升快如闪电

选择一个易于使用和方便的远程软件&#xff0c;可以提高团队的整体效率&#xff0c;减少加班&#xff0c;使整个团队更受益。互联网行业从产品经理、UI/从UX设计师到技术开发和测试人员&#xff0c;每一个环节都需要密切沟通和跟踪&#xff0c;在远程沟通中及时发现问题&#x…

power point导出pdf保留字体

在 slides 中用到非自带的字体&#xff0c;如 [1]&#xff0c;想导出成 pdf 文件&#xff08;因为导出成图&#xff0c;如 png&#xff0c;放大会蒙&#xff09;&#xff0c;并在别人电脑里也保留字体。除了让别人也装上相应字体&#xff0c;可以&#xff1a; 参考 [2]&#x…

“岗课赛证”融通的物联网综合实训室建设方案

一、概述 随着5G技术的普及应用和产业经济的革新发展,物联网产业所呈现的广阔前景带来了对创新型技术技能人才的迫切需求。高职院校物联网专业建设也因此转变为面向国家战略性新兴产业发展需求。当前,“岗位课程竞赛证书”融通的培育理念,是高职院校物联网人才培养和专业优化的…

Anomalib 图像异常检测算法

图像异常检测算法 1.简介1.1. 问题描述1.2. 挑战与需求 2. 图像异常检测定义2.1 异常的定义及类型2.2 图像数据中的异常 3. 图像异常检测技术研究现状4.方法5.安装和使用5.1 安装PyPI 安装本地安装 5.2 训练5.3 特征提取与&#xff08;预训练&#xff09;backones5.4 自定义数据…

大中小企业自招人力及劳务派遣全行业招聘来抖音招聘流量大效果佳

抖音直播招聘报白是通过抖音直播方式展现职位信息&#xff0c;并与求职者进行互动的招聘方式。在抖音平台上&#xff0c;企业或者人力资源公司可以通过直播的形式&#xff0c;将职位以视频直播的方式展现出来。通过抖音直播招聘报白&#xff0c;企业或者人力资源公司可以利用抖…

文本识别工具 TextSniper for Mac有哪些特点

TextSniper 是一款 macOS 平台上的文本提取工具&#xff0c;它可以将屏幕上的文字内容快速转换为可编辑的文本。无论是从图像、视频、PDF 文件还是其他类型的文档中提取文字&#xff0c;TextSniper 都提供了便捷的功能。 以下是 TextSniper的一些主要特点和功能&#xff1a; …

vue 插槽-默认插槽

vue 插槽-默认插槽 **创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day05\准备代码\07-插槽-默认插槽 vue --version vue create…

地铁大数据客流分析系统 设计与实现 计算机竞赛

文章目录 1 前言1.1 实现目的 2 数据集2.2 数据集概况2.3 数据字段 3 实现效果3.1 地铁数据整体概况3.2 平均指标3.3 地铁2018年9月开通运营的线路3.4 客流量相关统计3.4.1 线路客流量排行3.4.2 站点客流量排行3.4.3 入站客流排行3.4.4 整体客流随时间变化趋势3.4.5 不同线路客…

提升创意设计水平:十个必备的平面设计素材网站

1. 即时设计 即时设计是一款国产的专业级 UI 设计工具&#xff0c;别看它是一个专业的设计工具&#xff0c;具有齐全的设计工具与上万的优质设计资源&#xff0c;被称为优质的免费素材库毫不夸张。 即时设计 - 可实时协作的专业 UI 设计工具即时设计是一款支持在线协作的专业…

微信小程序之会议OA系统首页布局搭建与Mock数据交互

目录 前言 一、Flex 布局&#xff08; 分类 编程技术&#xff09; 1、Flex布局是什么&#xff1f; 2、基本概念 3、容器的属性 3.1 flex-direction属性 3.2 flex-wrap属性 3.3 flex-flow 3.4 justify-content属性 3.5 align-items属性 3.6 align-content属性 4、项目…

C1N短网址 | 核心专利(2) - 防止程序脚本访问短链接

1. 短链接介绍 短链接是一种缩短了URL长度的链接&#xff0c;通常由网址缩短服务提供商生成。短链接可以将长URL缩短为更短的URL&#xff0c;使其更易于分享和传播。短链接通常由一些字母、数字和特殊字符组成&#xff0c;可以通过点击或复制粘贴来访问原始的长URL。短链接在社…

PN8016 宽输出范围非隔离交直流转换芯片适用于非隔离的辅助电源

PN8016集成PFM控制器及800V高雪崩能力智能功率MOSFET&#xff0c;用于外围元器件极精简的小功率非隔离开关电源&#xff0c;输出电压可通过FB电阻调整。 PN8016内置800V高压启动与自供电模块&#xff0c;实现系统快速启动、超低待机、自供电功能。该芯片提供了完整的智能化保护…

IDC:到2027年,全球生成式AI支出将达到1430亿美元

全球著名信息调查咨询机构IDC在官网公布了一项调查&#xff0c;到2027年&#xff0c;全球生成式AI&#xff08;Generative AI&#xff0c;简称Gen AI&#xff09;支出将达到1430亿美元&#xff0c;5年复合年增长率为73.3%。 该支出包括&#xff1a;生成式AI的软件以及相关基础…

腾讯云服务器端口localhost可以访问,外部无法访问解决

搭建frp跳板&#xff0c;发现无法使用。ssh 连接不上。 主要检查2个东西&#xff1a; 1. ubuntu ufw系统防火墙。这个默认是关掉的 2. tencent这个防火墙规则设置后&#xff0c;还要设置到实例上。 以前不是这样的。就掉坑里了。 # systemctl rootVM-4-4-ubuntu:/lib/syst…

torch版本对应的torch_geometric与torch-sprse/cluster/scatter库的正确安装

torch_geometric官网&#xff1a; Installation — pytorch_geometric documentation 使用上述标红命令即可快速安装需要的包&#xff08;确定自己环境中安装的pytorch版本以及cuda版本&#xff0c;使用对应的命令即可&#xff09; 如安装的pytorch为1.60&#xff0c;cuda为1…

Docker 容器化(初学者的分享)

目录 一、什么是docker 二、docker的缺陷 三、简单的操作 一、首先配置一台虚拟机 二、安装Docker-CE 一、安装utils 二、 将 Docker 的软件源添加到 CentOS 的 yum 仓库中。这样可以通过 yum 命令来安装、更新和管理 Docker 相关的软件包。 三、将 download.docker.co…

水族店通过小程序商城经营的作用是什么

对水族店商家而言&#xff0c;市场高需求下&#xff0c;自然可售卖的产品非常广&#xff0c;除了鱼苗外&#xff0c;还有配套的鱼缸、鱼料、驱虫剂、氧气套具等。这些产品中部分是常需的&#xff0c;同时也有较强的同城属性。 在实际经营中&#xff0c;水族店商家经营难题也不…

SAP-MM-错误代码M7018 输入物料转移过账

业务场景&#xff1a; 在做库存转移&#xff0c;移动类型309时&#xff0c;报错&#xff1a;错误代码M7018 输入物料转移过账 这个报错是因为这种平移的物料&#xff0c;没有输入目的物料而产生。 也就是说你的旧物料是A&#xff0c;那么转移到新物料代码是哪个呢&#xff1f;…