Advertisement
Guest User

HDgghhfhjhgf

a guest
May 19th, 2019
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.55 KB | None | 0 0
  1. use samp::amx::Amx;
  2. use samp::cell::{AmxCell, AmxString, Ref};
  3. use samp::error::{AmxResult,AmxError};
  4. use samp::plugin::SampPlugin;
  5. use samp::{initialize_plugin, native};
  6.  
  7. use std::time::{Instant,Duration};
  8. use slab::Slab;
  9. use log::{info, error};
  10.  
  11. /// These are the types of arguments the plugin supports for passing on to the callback.
  12. #[derive(Debug, Clone)]
  13. enum PassedArgument {
  14. PrimitiveCell(i32),
  15. Str(Vec<u8>)
  16. }
  17.  
  18. /// The Timer struct represents a single scheduled timer
  19. #[derive(Debug, Clone)]
  20. struct Timer {
  21. next_trigger: Instant,
  22. interval: Option<Duration>,
  23. passed_arguments: Vec<PassedArgument>,
  24. amx_identifier: samp::amx::AmxIdent,
  25. amx_callback_index: samp::consts::AmxExecIdx,
  26. scheduled_for_removal: bool
  27. }
  28.  
  29. impl Timer {
  30. /// This function executes the callback provided to the `SetPreciseTimer` native.
  31. pub fn trigger(&mut self) -> AmxResult<()> {
  32.  
  33. // Get the AMX which scheduled the timer
  34. let amx = samp::amx::get(self.amx_identifier).ok_or(samp::error::AmxError::NotFound)?;
  35. let allocator = amx.allocator();
  36.  
  37. // Push the timer's arguments onto the AMX stack, in first-in-last-out order, i.e. reversed
  38. for param in self.passed_arguments.iter().rev() {
  39. match param {
  40. PassedArgument::PrimitiveCell(cell_value) => amx.push(cell_value)?,
  41. PassedArgument::Str(bytes) => {
  42. let buffer = allocator.allot_buffer(bytes.len() + 1)?;
  43. let amx_str = unsafe { AmxString::new(buffer, bytes) };
  44. amx.push(amx_str)?
  45. }
  46. }
  47. }
  48.  
  49. // Execute the callback (after pushing its arguments onto the stack)
  50. amx.exec(self.amx_callback_index)?;
  51.  
  52. // Return Result::Ok() with an empty value ("unit" ()) to indicate success.
  53. Ok(())
  54. }
  55. }
  56.  
  57. /// The plugin and its data: a list of scheduled timers
  58. struct PreciseTimers {
  59. timers: Slab<Timer>,
  60. }
  61.  
  62. impl PreciseTimers {
  63. /// This function is called from PAWN via the C foreign function interface.
  64. /// It returns the timer identifier or 0 in case of failure.
  65. /// ```
  66. /// native SetPreciseTimer(const callback_name[], const interval, const bool:repeat, const types_of_arguments[]="", ...);
  67. /// ```
  68. #[native(raw,name="SetPreciseTimer")]
  69. pub fn create(&mut self, amx: &Amx, mut args: samp::args::Args) -> AmxResult<i32> {
  70.  
  71. // Get the basic, mandatory timer parameters
  72. let callback_name = args.next::<AmxString>().ok_or(AmxError::Params)?;
  73. let interval = args.next::<i32>().ok_or(AmxError::Params)?;
  74. let repeat = args.next::<bool>().ok_or(AmxError::Params)?;
  75. let argument_type_lettters = args.next::<AmxString>().ok_or(AmxError::Params)?.to_bytes(); //iterator on AmxString would be more efficient if it was implemented
  76.  
  77. // Make sure they're sane
  78. if argument_type_lettters.len() != args.count() - 4 {
  79. error!("The amount of callback arguments passed ({}) does not match the length of the list of types ({}).",args.count() - 4, argument_type_lettters.len());
  80. return Err(AmxError::Params);
  81. }
  82.  
  83. if interval < 0 {
  84. error!("Invalid interval");
  85. return Err(AmxError::Params);
  86. }
  87.  
  88. let interval = Duration::from_millis(interval as u64);
  89.  
  90. // Get the arguments to pass to the callback
  91. let mut passed_arguments: Vec<PassedArgument> = Vec::with_capacity(argument_type_lettters.len());
  92.  
  93. for type_letter in argument_type_lettters {
  94. match type_letter {
  95. b'd' | b'i' | b'f' | b'b' => {
  96. let argument: Ref<i32> = args.next().ok_or(AmxError::Params)?;
  97. passed_arguments.push( PassedArgument::PrimitiveCell( *argument ) );
  98. },
  99. b's' => {
  100. let argument: Ref<i32> = args.next().ok_or(AmxError::Params)?;
  101. let amx_str = AmxString::from_raw(amx,argument.address())?;
  102. passed_arguments.push( PassedArgument::Str(amx_str.to_bytes()) );
  103. },
  104. _ => {
  105. error!("Unsupported argument type: {}",type_letter);
  106. return Err(AmxError::Params);
  107. }
  108. }
  109. }
  110.  
  111. // Find the callback by name and save its index
  112. let callback_index = amx.find_public(&callback_name.to_string())?;
  113.  
  114. // Add the timer to the list. This is safe for Slab::retain() even if SetPreciseTimer was called from a timer's callback.
  115. let timer = Timer {
  116. next_trigger: Instant::now() + interval,
  117. interval: if repeat { Some(interval) } else { None },
  118. passed_arguments: passed_arguments,
  119. amx_identifier: samp::amx::AmxIdent::from(amx.amx().as_ptr()),
  120. amx_callback_index: callback_index,
  121. scheduled_for_removal: false
  122. };
  123.  
  124. let key: usize = self.timers.insert(timer);
  125.  
  126. // Return the timer's slot in Slab<> incresed by 1, so that 0 signifies an invalid timer in PAWN
  127. Ok((key as i32) + 1)
  128. }
  129.  
  130. /// This function is called from PAWN via the C foreign function interface.
  131. /// Returns 0 if the timer does not exist.
  132. /// ```
  133. /// native DeletePreciseTimer(timer_number)
  134. /// ```
  135. #[native(name = "DeletePreciseTimer")]
  136. pub fn delete(&mut self, _: &Amx, timer_number: usize) -> AmxResult<i32> {
  137. // Subtract 1 from the passed timer_number (where 0=invalid) to get the actual Slab<> slot
  138. match self.timers.get_mut(timer_number - 1) {
  139. Some(timer) => {
  140. // We defer the removal so that we don't mess up the process_tick()->retain() iterator.
  141. timer.scheduled_for_removal = true;
  142. Ok(1)
  143. },
  144. None => Ok(0)
  145. }
  146. }
  147. }
  148.  
  149. impl SampPlugin for PreciseTimers {
  150. fn on_load(&mut self) {
  151. info!("net4game.com/samp-precise-timers by Brian Misiak loaded correctly.");
  152. }
  153.  
  154. #[inline(always)]
  155. fn process_tick(&mut self) {
  156. // Rust's Instant is monotonic and nondecreasing. 💖 Works even during NTP time adjustment.
  157. let now = Instant::now();
  158.  
  159. // 💀⚠ Because of FFI with C, Rust can't notice the simultaenous mutation of self.timers, but the iterator could get messed up in case of
  160. // Slab::retain() -> Timer::trigger() -> PAWN callback/ffi -> DeletePreciseTimer()->Slab::remove.
  161. // That's why the DeletePreciseTimer() schedules timers for deletion instead of doing it right away.
  162. // Slab::retain() is, however, okay with inserting new timers during its execution, even in case of reallocation when over capacity.
  163. self.timers.retain( |_key: usize, timer: &mut Timer| {
  164. if timer.next_trigger <= now {
  165. if timer.scheduled_for_removal {
  166. // REMOVE timer and don't execute its callback.
  167. return false;
  168. } else {
  169. // Execute the callback:
  170. if let Err(err) = timer.trigger() {
  171. error!("Error executing callback: {}",err);
  172. }
  173.  
  174. if let Some(interval) = timer.interval {
  175. timer.next_trigger = now + interval;
  176. // Keep the timer, because it repeats
  177. return true;
  178. } else {
  179. // REMOVE the timer. It got triggered and does not repeat
  180. return false;
  181. }
  182. }
  183. } else {
  184. // Keep the timer because it has yet to be triggered
  185. return true;
  186. }
  187. });
  188. }
  189. }
  190.  
  191. initialize_plugin!(
  192. natives: [
  193. PreciseTimers::delete,
  194. PreciseTimers::create,
  195. ],
  196. {
  197. samp::plugin::enable_process_tick();
  198.  
  199. // get the default samp logger (uses samp logprintf).
  200. let samp_logger = samp::plugin::logger().level(log::LevelFilter::Info); // logging info, warn and error messages
  201.  
  202. let _ = fern::Dispatch::new()
  203. .format(|callback, message, record| {
  204. callback.finish(format_args!("samp-precise-timers {}: {}", record.level().to_string().to_lowercase(), message))
  205. })
  206. .chain(samp_logger)
  207. .apply();
  208.  
  209. return PreciseTimers {
  210. timers: Slab::with_capacity(1000)
  211. };
  212. }
  213. );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement