Linux C编程:打造一个插件系统
title: ‘Linux C编程:打造一个插件系统’
date: 2017-03-07 21:16:36
tags: linux C
layout: post
comments: true
运行环境:linux
使用语言:c 或者c++
插件,很多人用过,比如游戏插件,编辑器插件这些,
最著名的就数魔兽大脚插件啦,还有vim插件啦,eclipse插件啦,等等
插件有很多种形式,最常见的就是so文件,在windows上就是各种的dll啦
下面就让我们来了解一下插件式编程吧〜〜
举个例子,你开发了一个游戏,一开始只有三个关卡,分享给了你的小伙伴玩
大家玩得很爽,但很快,游戏通关了,还想玩,这时候怎么办呢?
常规做法就是再加两关,然后编译之后,再发给大家,那么问题就来了
每次想加新关卡,都需要重新编译一次,再下载一次,这个流量嘛,1+1.1+1.2+1.3,每次加个0.1,都够你受的_
这个时候嘛,就要引入插件系统了
咱先来写个小程序
//main.c
#include <stdio.h>
#include "game.h" //关于游戏的定义void initGame()
{}
void play()
{printf("我打,我打,我打打打\n");
}
void loadMission()
{}
int main()
{initGame();//初始化游戏 int missionNum=loadMission();//读取关卡数while(1){int missionNo=getchar();//选择关卡missionNo=missionNo-'0';if(missionNo>missionNum){printf("没有此关卡!\n");}play();}}
以上就是一个可扩展的游戏架构,够简单吧〜
下面咱就开始设计这个游戏系统的插件吧
在这里,游戏关卡就是我们的插件,一个插件就是一个关卡
先讲讲一个插件的构造吧
首先是这个关卡的描述,比如名称,难度等
其次就是这个关卡游戏过程了
最后,就是玩关卡的人
按照以上描述,咱定几个结构体吧
//game.h
#include <stdio.h>
#include <string.h>
//游戏玩家描述
typedef struct _player
{int life;//玩家生命
}Player;
//游戏关卡描述
typedef struct _mission
{int level;//关卡难度char missionName[30];//关卡名称void (*process)(Player *);//游戏过程
}Mission;好了,下面就是一个插件的具体内容
```c
//game.c
#include "game.h"
const char name[]="第一关,插件入门";
const int level=1;void firstMission(Player *p)
{printf("oh my god ,somebody hurt me!\n");p->life--;printf("now my life is %d\n",p->life);sleep(1);
}
void gameInfo(Mission *m)
{m->level=level;int len=strlen(name);memcpy(m->missionName,name,len+1);//注意游戏名称不要太长m->process=firstMission;
}
Ok,以上就是一个关卡的所有东西了,虽然看着简单,不过还是建议动手敲敲_
那我们先把这个插件制作出来吧,免得一会忘记了,在终端下执行以下命令:
gcc game.c -fPIC -shared -o firstMission.so -ldl
linux和mac都一样
关卡设计好了,接下来就是怎么样读取我们做好的关卡了
现在实现咱们就loadMission()函数
Mission mission[50];
int loadMission()
{FILE * fp;fp = fopen("missionList.txt", "r");//读取关卡列表文件if (NULL== fp) {return 0;}int ret=0;char namelist[50][50];//最多50个插件,每个插件的名字长度最多50memset(namelist,0,sizeof(namelist));int count=0;while(fgets((char *)&namelist[count], 50, fp)) {ret=strlen(namelist[count]);//计算实际字符串长度if(namelist[count][ret-1]=='\n')namelist[count][ret-1]='\0';//fgets会读多一个换行,所以需要替换掉count++;} fclose(fp);ret=0;//用于累加错误次数for(int i=0;i<count;i++){const char *errmsg;dlerror(); // 清除错误void *m_hLib = (void *)dlopen(namelist[i], RTLD_LAZY);//读取插件if( (errmsg = dlerror()) != NULL ){//printf("err=%s\n",errmsg);打印错误ret++;//累加错误次数continue;}if( m_hLib == NULL ){ret++;//累加错误次数continue;}dlerror(); // 清除错误Info info = (Info)dlsym( m_hLib, "gameInfo" );//提取出插件里面的函数if( (errmsg = dlerror()) != NULL ){dlclose(m_hLib);printf("err=%s\n",errmsg);ret++;continue;}info(&mission[i]);//获取到关卡数据}ret=0;//关卡号//展示下关卡for(int i=0;i<count;i++){if(mission[i].level!=0){printf("%d.%s\n",ret+1,mission[i].missionName);/正式发布的时候就不要啦〜〜ret++;}}count=count-ret;//计算有效关卡数return count;}
好了,上面的代码,不难吧?咱编译一下,就可以跑了
linux下
gcc -o game main.c -Wl,rpath=.
mac下
gcc -o game main.c -Wl,rpath . -dynamic
再准备一个游戏菜单文件,执行
echo firstMission.so >missionlist.txt
好了,再执行
./game
一切顺利的话,你应该能看到自己的关卡列表了〜
接下来干嘛呢?我们现在仅仅是提取出了关卡的名字,还没开始玩游戏呢
以下是改动后的main.c文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "game.h" //关于游戏的定义Mission mission[50];
void initGame()
{memset(mission,0,sizeof(mission));
}int loadMission()
{FILE * fp;fp = fopen("missionList.txt", "r");//读取关卡列表文件if (NULL== fp) {return 0;}int ret=0;char namelist[50][50];//最多50个插件,每个插件的名字长度最多50memset(namelist,0,sizeof(namelist));int count=0;while(fgets((char *)&namelist[count], 50, fp)) {ret=strlen(namelist[count]);//计算实际字符串长度if(namelist[count][ret-1]=='\n')namelist[count][ret-1]='\0';//fgets会读多一个换行,所以需要替换掉count++;} fclose(fp);ret=0;for(int i=0;i<count;i++){const char *errmsg;dlerror(); // 清除错误void *m_hLib = (void *)dlopen(namelist[i], RTLD_LAZY);if( (errmsg = dlerror()) != NULL ){//printf("err=%s\n",errmsg);打印错误ret++;//累加错误次数continue;}if( m_hLib == NULL ){ret++;//累加错误次数continue;}dlerror(); // 清除错误Info info = (Info)dlsym( m_hLib, "gameInfo" );if( (errmsg = dlerror()) != NULL ){dlclose(m_hLib);printf("err=%s\n",errmsg);ret++;continue;}info(&mission[i]);}//展示下关卡for(int i=0;i<count;i++){if(mission[i].level!=0){printf("%d.%s\n",i+1,mission[i].missionName);//正式发布的时候就不要啦〜〜}}count=count-ret;//计算有效关卡数return count;}
int main()
{initGame();//初始化游戏 int missionNum=loadMission();//读取关卡数if(missionNum==0){printf("游戏异常退出\n");return -1;}//初始化一个角色,并满血Player p;while(1){p.life=100;printf("请选择关卡\n");for(int i=0;i<missionNum;i++){printf("%d.%s\n",i+1,mission[i].missionName);}int missionNo=getchar();//选择关卡missionNo=missionNo-'0';if(missionNo>missionNum || missionNo<1){printf("没有此关卡!\n");continue;}//读取来的关卡肯定是有难度等级的,没有就是无效关卡if(mission[missionNo-1].level!=0){while(p.life>0)mission[missionNo-1].process(&p);}printf("Game Over!\n\n\n");}}
这就是一个完整的游戏啦,感兴趣的同学可以继续扩展哟〜〜后面就是不断得出扩展啦
你问我怎么扩展?
好吧,自己把game.c里面的内容改改,然后编译出来,换个名字,然后在missionlist.txt里面加一行,就ok了
还不懂?那你只能给我打赏了_
相关文章:
Linux C编程:打造一个插件系统
title: ‘Linux C编程:打造一个插件系统’ date: 2017-03-07 21:16:36 tags: linux C layout: post comments: true 运行环境:linux 使用语言:c 或者c 插件,很多人用过,比如游戏插件,编辑器插件这些, 最著…...
基于毫米波生物感知雷达+STM32设计的独居老人居家监护系统(微信小程序)(192)
基于毫米波生物感知雷达设计的独居老人居家监护系统(微信小程序)(192) 文章目录 一、前言1.1 项目介绍【1】项目功能介绍【2】项目硬件模块组成1.2 设计思路【1】整体设计思路【2】60G毫米波生物感知雷达原理【3】ESP8266模块配置【4】供电方式1.3 项目开发背景【1】选题的意义…...
C++——类和对象(下)
目录 一、再探构造函数 1.基本定义以及用法 2.必须在初始化列表初始化的成员变量 3.成员变量声明位置的缺省值(C11) 4.成员变量初始化顺序 二、隐式类型转换 三、static成员 四、友元 五、内部类 六、匿名对象 七、日期类实现 一、再探构造函数…...
Android中集成前端页面探索(Capacitor 或 Cordova 插件)待完善......
探索目标:Android中集成前端页面 之前使用的webview加载html页面,使用bridge的方式进行原生安卓和html页面的通信的方式,探索capacitor-android插件是如何操作的 capacitor-android用途 Capacitor 是一个用于构建现代跨平台应用程序的开源框…...
玩转CSS:用ul li +JS 模拟select,避坑浏览器不兼容。
玩转CSS:用ul li JS 模拟select,避坑浏览器不兼容。 在前端的工作中,经常会遇到 selcet控件,但我们用css来写它的样式时候,总是不那么令人满意,各种浏览器不兼容啊有没有? 那么,我…...
介绍下PolarDB
业务中用的是阿里云自研的PolarDB,分析下PolarDB的架构。 认识PolarDB 介绍 PolarDB是阿里云自研的,兼容MySQL、PostageSQL以及支持MPP的PolarDB-X的高可用、高扩展性的数据库。 架构 部署 云起实验室 - 阿里云开发者社区 - 阿里云 (aliyun.com) 数…...
基于微信小程序+SpringBoot+Vue的儿童预防接种预约系统(带1w+文档)
基于微信小程序SpringBootVue的儿童预防接种预约系统(带1w文档) 基于微信小程序SpringBootVue的儿童预防接种预约系统(带1w文档) 开发合适的儿童预防接种预约微信小程序,可以方便管理人员对儿童预防接种预约微信小程序的管理,提高信息管理工作效率及查询…...
go语言day15 goroutine
Golang-100-Days/Day16-20(Go语言基础进阶)/day17_Go语言并发Goroutine.md at master rubyhan1314/Golang-100-Days GitHub 第2讲-调度器的由来和分析_哔哩哔哩_bilibili 一个进程最多可以创建多少个线程?-CSDN博客 引入协程 go语言中内置了协程goroutine&#…...
Mindspore框架循环神经网络RNN模型实现情感分类|(六)模型加载和推理(情感分类模型资源下载)
Mindspore框架循环神经网络RNN模型实现情感分类 Mindspore框架循环神经网络RNN模型实现情感分类|(一)IMDB影评数据集准备 Mindspore框架循环神经网络RNN模型实现情感分类|(二)预训练词向量 Mindspore框架循环神经网络RNN模型实现…...
System类
System类常见方法 ① exit 退出当前程序 public static void main(String[] args) {System.out.println("ok1");//0表示状态,即正常退出System.exit(0);System.out.println("ok2");} ② arraycopy 复制数组元素 复制的数组元素个数必须<原数…...
【前端 02】新浪新闻项目-初步使用CSS来排版
在今天的博文中,我们将围绕“新浪新闻”项目,深入探讨HTML和CSS在网页制作中的基础应用。通过具体实例,我们将学习如何设置图片、标题、超链接以及文本排版,同时了解CSS的引入方式和选择器优先级,以及视频和音频标签的…...
HarmonyOS和OpenHarmony区别联系
前言 相信我们在刚开始接触鸿蒙开发的时候经常看到HarmonyOS和OpenHarmony频繁的出现在文章和文档之中,那么这两个名词分别是什么意思,他们之间又有什么联系呢?本文将通过现有的文章和网站内容并与Google的AOSP和Android做对比,带…...
llama模型,nano
目录 llama模型 Llama模型性能评测 nano模型是什么 Gemini Nano模型 参数量 MMLU、GPQA、HumanEval 1. MMLU(Massive Multi-task Language Understanding) 2. GPQA(Grade School Physics Question Answering) 3. HumanEval llama模型 Large Language Model AI Ll…...
ElasticSearch的应用场景和优势
ElasticSearch是一个开源的分布式搜索和分析引擎,它以其高性能、可扩展性和实时性在多个领域得到了广泛应用。以下是ElasticSearch的主要应用场景和优势: 应用场景 实时搜索: ElasticSearch以其快速、可扩展和实时的特性,成为实…...
git 、shell脚本
git 文件版本控制 安装git yum -y install git 创建仓库 将文件提交到暂存 git add . #将暂存区域的文件提交仓库 git commit -m "说明" #推送到远程仓库 git push #获取远程仓库的更新 git pull #克隆远程仓库 git clone #分支,提高代码的灵活性 #检查分…...
阿里云服务器 篇六:GitHub镜像网站
文章目录 系列文章搭建镜像网站的2种方式使用 Web 抓取工具 (Spider 技术)使用 Web 代理服务器使用 nginx 搭建GitHub镜像网站基础环境搭建添加对 github.com 的转发配置添加对 raw.githubusercontent.com 的转发配置配置更改注意事项(可选)缓存优化为新增设的二级域名配置DN…...
强化学习学习(三)收敛性证明与DDPG
文章目录 证明收敛? Deep RL with Q-FunctionsDouble Q-Learning理论上的解法实际上的解法 DDPG: Q-Learning with continuous actionsAdvanced tips for Q-Learning 证明收敛? 对于Value迭代:不动点证明的思路 首先定义一个算子 B : B V ma…...
培养前端工程化思维,不要让一行代码毁了整个程序
看《阿丽亚娜 5 号(Ariane 5)火箭爆炸》有感。 1、动手写项目之前,先进行全局性代码逻辑思考,将该做的事情,一些细节,统一建立标准,避免为以后埋雷。 2、避免使用不必要或无意义的代码、注释。…...
电子文件怎么盖章?
电子文件怎么盖章?电子文件盖章是数字化办公中常见的操作,包括盖电子公章和电子骑缝章。以下是针对这两种情况的详细步骤: 一、盖电子公章 方法一:使用专业软件 选择软件:选择一款专业的电子签名或PDF编辑软件&…...
IDEA在编译的时候报Error: java: 找不到符号符号: 变量 log lombok失效问题
错误描述 idea因为lombok的报错: java: You arent using a compiler supported by lombok, so lombok will not work and has been disabled.Your processor is: com.sun.proxy.$Proxy8Lombok supports: sun/apple javac 1.6, ECJ 原因:这是由于Lombok的版本过低的…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
